19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.internal.util.ArrayUtils;
20776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport com.android.internal.util.GrowingArrayUtils;
21776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski
22776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport libcore.util.EmptyArray;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Array;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* package */ abstract class SpannableStringInternal
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ SpannableStringInternal(CharSequence source,
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                          int start, int end) {
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start == 0 && end == source.length())
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mText = source.toString();
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mText = source.toString().substring(start, end);
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
35776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mSpans = EmptyArray.OBJECT;
3683549088c643586aa6013d947ad2c21464a3878eRaph Levien        // Invariant: mSpanData.length = mSpans.length * COLUMNS
37776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mSpanData = EmptyArray.INT;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned) {
40fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (source instanceof SpannableStringInternal) {
41fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                copySpans((SpannableStringInternal) source, start, end);
42fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            } else {
43fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                copySpans((Spanned) source, start, end);
44fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            }
45fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        }
46fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    }
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
48fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    /**
49fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * Copies another {@link Spanned} object's spans between [start, end] into this object.
50fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     *
51fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @param src Source object to copy from.
52fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @param start Start index in the source object.
53fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @param end End index in the source object.
54fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     */
55fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    private final void copySpans(Spanned src, int start, int end) {
56fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        Object[] spans = src.getSpans(start, end, Object.class);
57fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir
58fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        for (int i = 0; i < spans.length; i++) {
59fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            int st = src.getSpanStart(spans[i]);
60fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            int en = src.getSpanEnd(spans[i]);
61fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            int fl = src.getSpanFlags(spans[i]);
62fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir
63fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (st < start)
64fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                st = start;
65fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (en > end)
66fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                en = end;
67fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir
68fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            setSpan(spans[i], st - start, en - start, fl);
69fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        }
70fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    }
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
72fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    /**
73fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * Copies a {@link SpannableStringInternal} object's spans between [start, end] into this
74fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * object.
75fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     *
76fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @param src Source object to copy from.
77fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @param start Start index in the source object.
78fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @param end End index in the source object.
79fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     */
80fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    private final void copySpans(SpannableStringInternal src, int start, int end) {
81fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        if (start == 0 && end == src.length()) {
82fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length);
83fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpanData = new int[src.mSpanData.length];
84fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpanCount = src.mSpanCount;
85fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length);
86fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length);
87fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        } else {
88fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            int count = 0;
89fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            int[] srcData = src.mSpanData;
90fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            int limit = src.mSpanCount;
91fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            for (int i = 0; i < limit; i++) {
92fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                int spanStart = srcData[i * COLUMNS + START];
93fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                int spanEnd = srcData[i * COLUMNS + END];
94fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
95fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                count++;
96fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            }
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
98fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (count == 0) return;
99fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir
100fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            Object[] srcSpans = src.mSpans;
101fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpanCount = count;
102fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
10383549088c643586aa6013d947ad2c21464a3878eRaph Levien            mSpanData = new int[mSpans.length * COLUMNS];
104fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            for (int i = 0, j = 0; i < limit; i++) {
105fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                int spanStart = srcData[i * COLUMNS + START];
106fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                int spanEnd = srcData[i * COLUMNS + END];
107fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
108fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                if (spanStart < start) spanStart = start;
109fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                if (spanEnd > end) spanEnd = end;
110fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir
111fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                mSpans[j] = srcSpans[i];
112fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                mSpanData[j * COLUMNS + START] = spanStart - start;
113fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                mSpanData[j * COLUMNS + END] = spanEnd - start;
114fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                mSpanData[j * COLUMNS + FLAGS] = srcData[i * COLUMNS + FLAGS];
115fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir                j++;
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
120fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    /**
121fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * Checks if [spanStart, spanEnd] interval is excluded from [start, end].
122fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     *
123fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     * @return True if excluded, false if included.
124fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir     */
125fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    private final boolean isOutOfCopyRange(int start, int end, int spanStart, int spanEnd) {
126fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        if (spanStart > end || spanEnd < start) return true;
127fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        if (spanStart != spanEnd && start != end) {
128fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (spanStart == end || spanEnd == start) return true;
129fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        }
130fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir        return false;
131fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir    }
132fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int length() {
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mText.length();
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final char charAt(int i) {
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mText.charAt(i);
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final String toString() {
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mText;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* subclasses must do subSequence() to preserve type */
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final void getChars(int start, int end, char[] dest, int off) {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mText.getChars(start, end, dest, off);
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ void setSpan(Object what, int start, int end, int flags) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int nstart = start;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int nend = end;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        checkRange("setSpan", start, end);
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) {
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start != 0 && start != length()) {
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char c = charAt(start - 1);
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (c != '\n')
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new RuntimeException(
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            "PARAGRAPH span must start at paragraph boundary" +
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            " (" + start + " follows " + c + ")");
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end != 0 && end != length()) {
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char c = charAt(end - 1);
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (c != '\n')
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new RuntimeException(
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            "PARAGRAPH span must end at paragraph boundary" +
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            " (" + end + " follows " + c + ")");
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int ostart = data[i * COLUMNS + START];
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int oend = data[i * COLUMNS + END];
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                data[i * COLUMNS + START] = start;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                data[i * COLUMNS + END] = end;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                data[i * COLUMNS + FLAGS] = flags;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendSpanChanged(what, ostart, oend, nstart, nend);
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSpanCount + 1 >= mSpans.length) {
196776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            Object[] newtags = ArrayUtils.newUnpaddedObjectArray(
197776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                    GrowingArrayUtils.growSize(mSpanCount));
198776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            int[] newdata = new int[newtags.length * 3];
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mSpans, 0, newtags, 0, mSpanCount);
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mSpanData, 0, newdata, 0, mSpanCount * 3);
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSpans = newtags;
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSpanData = newdata;
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpans[mSpanCount] = what;
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanData[mSpanCount * COLUMNS + START] = start;
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanData[mSpanCount * COLUMNS + END] = end;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanData[mSpanCount * COLUMNS + FLAGS] = flags;
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanCount++;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (this instanceof Spannable)
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sendSpanAdded(what, nstart, nend);
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ void removeSpan(Object what) {
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int ostart = data[i * COLUMNS + START];
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int oend = data[i * COLUMNS + END];
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int c = count - (i + 1);
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(spans, i + 1, spans, i, c);
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(data, (i + 1) * COLUMNS,
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                 data, i * COLUMNS, c * COLUMNS);
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSpanCount--;
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendSpanRemoved(what, ostart, oend);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getSpanStart(Object what) {
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return data[i * COLUMNS + START];
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getSpanEnd(Object what) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return data[i * COLUMNS + END];
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getSpanFlags(Object what) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return data[i * COLUMNS + FLAGS];
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return 0;
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = 0;
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int spanCount = mSpanCount;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] ret = null;
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object ret1 = null;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < spanCount; i++) {
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int spanStart = data[i * COLUMNS + START];
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int spanEnd = data[i * COLUMNS + END];
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanStart > queryEnd) {
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanEnd < queryStart) {
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanStart != spanEnd && queryStart != queryEnd) {
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (spanStart == queryEnd) {
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    continue;
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (spanEnd == queryStart) {
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    continue;
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
312fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik            // verify span class as late as possible, since it is expensive
313fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (kind != null && kind != Object.class && !kind.isInstance(spans[i])) {
314fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik                continue;
315fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik            }
316fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (count == 0) {
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret1 = spans[i];
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                count++;
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (count == 1) {
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret[0] = ret1;
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int prio = data[i * COLUMNS + FLAGS] & Spanned.SPAN_PRIORITY;
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (prio != 0) {
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int j;
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (j = 0; j < count; j++) {
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int p = getSpanFlags(ret[j]) & Spanned.SPAN_PRIORITY;
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (prio > p) {
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    System.arraycopy(ret, j, ret, j + 1, count - j);
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret[j] = spans[i];
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    count++;
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret[count++] = spans[i];
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == 0) {
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (T[]) ArrayUtils.emptyArray(kind);
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == 1) {
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret = (Object[]) Array.newInstance(kind, 1);
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret[0] = ret1;
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (T[]) ret;
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == ret.length) {
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (T[]) ret;
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] nret = (Object[]) Array.newInstance(kind, count);
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        System.arraycopy(ret, 0, nret, 0, count);
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (T[]) nret;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int nextSpanTransition(int start, int limit, Class kind) {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (kind == null) {
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            kind = Object.class;
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int st = data[i * COLUMNS + START];
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int en = data[i * COLUMNS + END];
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (st > start && st < limit && kind.isInstance(spans[i]))
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                limit = st;
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (en > start && en < limit && kind.isInstance(spans[i]))
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                limit = en;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return limit;
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendSpanAdded(Object what, int start, int end) {
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int n = recip.length;
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i++) {
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recip[i].onSpanAdded((Spannable) this, what, start, end);
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendSpanRemoved(Object what, int start, int end) {
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int n = recip.length;
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i++) {
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recip[i].onSpanRemoved((Spannable) this, what, start, end);
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendSpanChanged(Object what, int s, int e, int st, int en) {
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       SpanWatcher.class);
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int n = recip.length;
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i++) {
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recip[i].onSpanChanged((Spannable) this, what, s, e, st, en);
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String region(int start, int end) {
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return "(" + start + " ... " + end + ")";
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void checkRange(final String operation, int start, int end) {
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end < start) {
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IndexOutOfBoundsException(operation + " " +
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                region(start, end) +
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                " has end before start");
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = length();
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start > len || end > len) {
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IndexOutOfBoundsException(operation + " " +
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                region(start, end) +
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                " ends beyond length " + len);
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start < 0 || end < 0) {
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IndexOutOfBoundsException(operation + " " +
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                region(start, end) +
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                " starts before 0");
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4409b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    // Same as SpannableStringBuilder
4419b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    @Override
4429b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    public boolean equals(Object o) {
4439b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        if (o instanceof Spanned &&
4449b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                toString().equals(o.toString())) {
4451d3c4b396a393515e738cb72782ef564362420faChet Haase            Spanned other = (Spanned) o;
4469b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            // Check span data
4471d3c4b396a393515e738cb72782ef564362420faChet Haase            Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
4489b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            if (mSpanCount == otherSpans.length) {
4499b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                for (int i = 0; i < mSpanCount; ++i) {
4509b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                    Object thisSpan = mSpans[i];
4519b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                    Object otherSpan = otherSpans[i];
4521d3c4b396a393515e738cb72782ef564362420faChet Haase                    if (thisSpan == this) {
4531d3c4b396a393515e738cb72782ef564362420faChet Haase                        if (other != otherSpan ||
4541d3c4b396a393515e738cb72782ef564362420faChet Haase                                getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
4551d3c4b396a393515e738cb72782ef564362420faChet Haase                                getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
4561d3c4b396a393515e738cb72782ef564362420faChet Haase                                getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
4571d3c4b396a393515e738cb72782ef564362420faChet Haase                            return false;
4581d3c4b396a393515e738cb72782ef564362420faChet Haase                        }
4591d3c4b396a393515e738cb72782ef564362420faChet Haase                    } else if (!thisSpan.equals(otherSpan) ||
4601d3c4b396a393515e738cb72782ef564362420faChet Haase                            getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
4611d3c4b396a393515e738cb72782ef564362420faChet Haase                            getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
4621d3c4b396a393515e738cb72782ef564362420faChet Haase                            getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
4639b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                        return false;
4649b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                    }
4659b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                }
4669b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                return true;
4679b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            }
4689b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        }
4699b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        return false;
4709b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    }
4719b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase
4729b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    // Same as SpannableStringBuilder
4739b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    @Override
4749b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    public int hashCode() {
4759b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        int hash = toString().hashCode();
4769b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        hash = hash * 31 + mSpanCount;
4779b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        for (int i = 0; i < mSpanCount; ++i) {
4789b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            Object span = mSpans[i];
4791d3c4b396a393515e738cb72782ef564362420faChet Haase            if (span != this) {
4801d3c4b396a393515e738cb72782ef564362420faChet Haase                hash = hash * 31 + span.hashCode();
4811d3c4b396a393515e738cb72782ef564362420faChet Haase            }
4829b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            hash = hash * 31 + getSpanStart(span);
4839b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            hash = hash * 31 + getSpanEnd(span);
4849b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            hash = hash * 31 + getSpanFlags(span);
4859b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        }
4869b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        return hash;
4879b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    }
4889b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mText;
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Object[] mSpans;
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[] mSpanData;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mSpanCount;
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static final Object[] EMPTY = new Object[0];
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START = 0;
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int END = 1;
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int FLAGS = 2;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int COLUMNS = 3;
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
501