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;
36575284af7c1370bd732e3db77ff55f0b57566d91Raph 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) {
56fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed 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]);
61fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed 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
68a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir            setSpan(spans[i], st - start, en - start, fl, false/*enforceParagraph*/);
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) {
81fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed 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 {
88fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            int count = 0;
89fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            int[] srcData = src.mSpanData;
90fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            int limit = src.mSpanCount;
91fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            for (int i = 0; i < limit; i++) {
92fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir                int spanStart = srcData[i * COLUMNS + START];
93fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir                int spanEnd = srcData[i * COLUMNS + END];
94fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
95fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir                count++;
96fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            }
97fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir
98fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            if (count == 0) return;
99fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir
100fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed Sinir            Object[] srcSpans = src.mSpans;
101fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpanCount = count;
102fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
103575284af7c1370bd732e3db77ff55f0b57566d91Raph 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];
107fa4a02c13f29a6d378e02015c2407eba283827c0Siyamed 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) {
152a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir        setSpan(what, start, end, flags, true/*enforceParagraph*/);
153a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir    }
154a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir
155a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir    private boolean isIndexFollowsNextLine(int index) {
156a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir        return index != 0 && index != length() && charAt(index - 1) != '\n';
157a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir    }
158a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir
159a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir    private void setSpan(Object what, int start, int end, int flags, boolean enforceParagraph) {
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int nstart = start;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int nend = end;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        checkRange("setSpan", start, end);
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) {
166a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir            if (isIndexFollowsNextLine(start)) {
167a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                if (!enforceParagraph) {
168a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                    // do not set the span
169a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                    return;
170a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                }
171a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                throw new RuntimeException("PARAGRAPH span must start at paragraph boundary"
172a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                        + " (" + start + " follows " + charAt(start - 1) + ")");
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
175a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir            if (isIndexFollowsNextLine(end)) {
176a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                if (!enforceParagraph) {
177a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                    // do not set the span
178a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                    return;
179a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                }
180a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                throw new RuntimeException("PARAGRAPH span must end at paragraph boundary"
181a6284a20b590be2be52e207174d3d0a980c77180Siyamed Sinir                        + " (" + end + " follows " + charAt(end - 1) + ")");
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int ostart = data[i * COLUMNS + START];
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int oend = data[i * COLUMNS + END];
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                data[i * COLUMNS + START] = start;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                data[i * COLUMNS + END] = end;
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                data[i * COLUMNS + FLAGS] = flags;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendSpanChanged(what, ostart, oend, nstart, nend);
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSpanCount + 1 >= mSpans.length) {
204776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            Object[] newtags = ArrayUtils.newUnpaddedObjectArray(
205776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                    GrowingArrayUtils.growSize(mSpanCount));
206776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            int[] newdata = new int[newtags.length * 3];
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mSpans, 0, newtags, 0, mSpanCount);
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mSpanData, 0, newdata, 0, mSpanCount * 3);
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSpans = newtags;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSpanData = newdata;
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpans[mSpanCount] = what;
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanData[mSpanCount * COLUMNS + START] = start;
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanData[mSpanCount * COLUMNS + END] = end;
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanData[mSpanCount * COLUMNS + FLAGS] = flags;
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpanCount++;
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (this instanceof Spannable)
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sendSpanAdded(what, nstart, nend);
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ void removeSpan(Object what) {
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int ostart = data[i * COLUMNS + START];
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int oend = data[i * COLUMNS + END];
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int c = count - (i + 1);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(spans, i + 1, spans, i, c);
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(data, (i + 1) * COLUMNS,
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                 data, i * COLUMNS, c * COLUMNS);
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSpanCount--;
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendSpanRemoved(what, ostart, oend);
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getSpanStart(Object what) {
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return data[i * COLUMNS + START];
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getSpanEnd(Object what) {
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return data[i * COLUMNS + END];
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getSpanFlags(Object what) {
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spans[i] == what) {
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return data[i * COLUMNS + FLAGS];
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return 0;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = 0;
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int spanCount = mSpanCount;
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] ret = null;
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object ret1 = null;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < spanCount; i++) {
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int spanStart = data[i * COLUMNS + START];
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int spanEnd = data[i * COLUMNS + END];
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
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            if (spanStart != spanEnd && queryStart != queryEnd) {
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (spanStart == queryEnd) {
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    continue;
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (spanEnd == queryStart) {
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    continue;
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
320fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik            // verify span class as late as possible, since it is expensive
321fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir            if (kind != null && kind != Object.class && !kind.isInstance(spans[i])) {
322fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik                continue;
323fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik            }
324fc121c2430cd27c0d9b655105a9ab1c261c10bdcChris Craik
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (count == 0) {
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret1 = spans[i];
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                count++;
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (count == 1) {
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret[0] = ret1;
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int prio = data[i * COLUMNS + FLAGS] & Spanned.SPAN_PRIORITY;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (prio != 0) {
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int j;
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (j = 0; j < count; j++) {
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int p = getSpanFlags(ret[j]) & Spanned.SPAN_PRIORITY;
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (prio > p) {
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    System.arraycopy(ret, j, ret, j + 1, count - j);
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret[j] = spans[i];
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    count++;
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret[count++] = spans[i];
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == 0) {
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (T[]) ArrayUtils.emptyArray(kind);
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == 1) {
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret = (Object[]) Array.newInstance(kind, 1);
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret[0] = ret1;
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (T[]) ret;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == ret.length) {
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (T[]) ret;
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] nret = (Object[]) Array.newInstance(kind, count);
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        System.arraycopy(ret, 0, nret, 0, count);
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (T[]) nret;
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int nextSpanTransition(int start, int limit, Class kind) {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = mSpanCount;
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = mSpans;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] data = mSpanData;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (kind == null) {
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            kind = Object.class;
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int st = data[i * COLUMNS + START];
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int en = data[i * COLUMNS + END];
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (st > start && st < limit && kind.isInstance(spans[i]))
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                limit = st;
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (en > start && en < limit && kind.isInstance(spans[i]))
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                limit = en;
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return limit;
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendSpanAdded(Object what, int start, int end) {
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int n = recip.length;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i++) {
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recip[i].onSpanAdded((Spannable) this, what, start, end);
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendSpanRemoved(Object what, int start, int end) {
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int n = recip.length;
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i++) {
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recip[i].onSpanRemoved((Spannable) this, what, start, end);
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendSpanChanged(Object what, int s, int e, int st, int en) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       SpanWatcher.class);
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int n = recip.length;
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i++) {
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recip[i].onSpanChanged((Spannable) this, what, s, e, st, en);
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String region(int start, int end) {
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return "(" + start + " ... " + end + ")";
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void checkRange(final String operation, int start, int end) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end < start) {
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IndexOutOfBoundsException(operation + " " +
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                region(start, end) +
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                " has end before start");
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = length();
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start > len || end > len) {
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IndexOutOfBoundsException(operation + " " +
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                region(start, end) +
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                " ends beyond length " + len);
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start < 0 || end < 0) {
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IndexOutOfBoundsException(operation + " " +
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                region(start, end) +
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                " starts before 0");
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4489b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    // Same as SpannableStringBuilder
4499b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    @Override
4509b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    public boolean equals(Object o) {
4519b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        if (o instanceof Spanned &&
4529b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                toString().equals(o.toString())) {
4531d3c4b396a393515e738cb72782ef564362420faChet Haase            Spanned other = (Spanned) o;
4549b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            // Check span data
4551d3c4b396a393515e738cb72782ef564362420faChet Haase            Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
4569b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            if (mSpanCount == otherSpans.length) {
4579b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                for (int i = 0; i < mSpanCount; ++i) {
4589b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                    Object thisSpan = mSpans[i];
4599b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                    Object otherSpan = otherSpans[i];
4601d3c4b396a393515e738cb72782ef564362420faChet Haase                    if (thisSpan == this) {
4611d3c4b396a393515e738cb72782ef564362420faChet Haase                        if (other != otherSpan ||
4621d3c4b396a393515e738cb72782ef564362420faChet Haase                                getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
4631d3c4b396a393515e738cb72782ef564362420faChet Haase                                getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
4641d3c4b396a393515e738cb72782ef564362420faChet Haase                                getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
4651d3c4b396a393515e738cb72782ef564362420faChet Haase                            return false;
4661d3c4b396a393515e738cb72782ef564362420faChet Haase                        }
4671d3c4b396a393515e738cb72782ef564362420faChet Haase                    } else if (!thisSpan.equals(otherSpan) ||
4681d3c4b396a393515e738cb72782ef564362420faChet Haase                            getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
4691d3c4b396a393515e738cb72782ef564362420faChet Haase                            getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
4701d3c4b396a393515e738cb72782ef564362420faChet Haase                            getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
4719b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                        return false;
4729b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                    }
4739b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                }
4749b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase                return true;
4759b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            }
4769b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        }
4779b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        return false;
4789b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    }
4799b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase
4809b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    // Same as SpannableStringBuilder
4819b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    @Override
4829b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    public int hashCode() {
4839b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        int hash = toString().hashCode();
4849b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        hash = hash * 31 + mSpanCount;
4859b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        for (int i = 0; i < mSpanCount; ++i) {
4869b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            Object span = mSpans[i];
4871d3c4b396a393515e738cb72782ef564362420faChet Haase            if (span != this) {
4881d3c4b396a393515e738cb72782ef564362420faChet Haase                hash = hash * 31 + span.hashCode();
4891d3c4b396a393515e738cb72782ef564362420faChet Haase            }
4909b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            hash = hash * 31 + getSpanStart(span);
4919b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            hash = hash * 31 + getSpanEnd(span);
4929b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase            hash = hash * 31 + getSpanFlags(span);
4939b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        }
4949b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase        return hash;
4959b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase    }
4969b985721dafaa28d1aa34499d23a83afc6d0a2faChet Haase
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mText;
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Object[] mSpans;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[] mSpanData;
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mSpanCount;
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static final Object[] EMPTY = new Object[0];
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START = 0;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int END = 1;
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int FLAGS = 2;
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int COLUMNS = 3;
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
509