1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.text;
18
19import com.android.internal.util.ArrayUtils;
20
21import java.lang.reflect.Array;
22
23/* package */ abstract class SpannableStringInternal
24{
25    /* package */ SpannableStringInternal(CharSequence source,
26                                          int start, int end) {
27        if (start == 0 && end == source.length())
28            mText = source.toString();
29        else
30            mText = source.toString().substring(start, end);
31
32        int initial = ArrayUtils.idealIntArraySize(0);
33        mSpans = new Object[initial];
34        mSpanData = new int[initial * 3];
35
36        if (source instanceof Spanned) {
37            Spanned sp = (Spanned) source;
38            Object[] spans = sp.getSpans(start, end, Object.class);
39
40            for (int i = 0; i < spans.length; i++) {
41                int st = sp.getSpanStart(spans[i]);
42                int en = sp.getSpanEnd(spans[i]);
43                int fl = sp.getSpanFlags(spans[i]);
44
45                if (st < start)
46                    st = start;
47                if (en > end)
48                    en = end;
49
50                setSpan(spans[i], st - start, en - start, fl);
51            }
52        }
53    }
54
55    public final int length() {
56        return mText.length();
57    }
58
59    public final char charAt(int i) {
60        return mText.charAt(i);
61    }
62
63    public final String toString() {
64        return mText;
65    }
66
67    /* subclasses must do subSequence() to preserve type */
68
69    public final void getChars(int start, int end, char[] dest, int off) {
70        mText.getChars(start, end, dest, off);
71    }
72
73    /* package */ void setSpan(Object what, int start, int end, int flags) {
74        int nstart = start;
75        int nend = end;
76
77        checkRange("setSpan", start, end);
78
79        if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) {
80            if (start != 0 && start != length()) {
81                char c = charAt(start - 1);
82
83                if (c != '\n')
84                    throw new RuntimeException(
85                            "PARAGRAPH span must start at paragraph boundary" +
86                            " (" + start + " follows " + c + ")");
87            }
88
89            if (end != 0 && end != length()) {
90                char c = charAt(end - 1);
91
92                if (c != '\n')
93                    throw new RuntimeException(
94                            "PARAGRAPH span must end at paragraph boundary" +
95                            " (" + end + " follows " + c + ")");
96            }
97        }
98
99        int count = mSpanCount;
100        Object[] spans = mSpans;
101        int[] data = mSpanData;
102
103        for (int i = 0; i < count; i++) {
104            if (spans[i] == what) {
105                int ostart = data[i * COLUMNS + START];
106                int oend = data[i * COLUMNS + END];
107
108                data[i * COLUMNS + START] = start;
109                data[i * COLUMNS + END] = end;
110                data[i * COLUMNS + FLAGS] = flags;
111
112                sendSpanChanged(what, ostart, oend, nstart, nend);
113                return;
114            }
115        }
116
117        if (mSpanCount + 1 >= mSpans.length) {
118            int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1);
119            Object[] newtags = new Object[newsize];
120            int[] newdata = new int[newsize * 3];
121
122            System.arraycopy(mSpans, 0, newtags, 0, mSpanCount);
123            System.arraycopy(mSpanData, 0, newdata, 0, mSpanCount * 3);
124
125            mSpans = newtags;
126            mSpanData = newdata;
127        }
128
129        mSpans[mSpanCount] = what;
130        mSpanData[mSpanCount * COLUMNS + START] = start;
131        mSpanData[mSpanCount * COLUMNS + END] = end;
132        mSpanData[mSpanCount * COLUMNS + FLAGS] = flags;
133        mSpanCount++;
134
135        if (this instanceof Spannable)
136            sendSpanAdded(what, nstart, nend);
137    }
138
139    /* package */ void removeSpan(Object what) {
140        int count = mSpanCount;
141        Object[] spans = mSpans;
142        int[] data = mSpanData;
143
144        for (int i = count - 1; i >= 0; i--) {
145            if (spans[i] == what) {
146                int ostart = data[i * COLUMNS + START];
147                int oend = data[i * COLUMNS + END];
148
149                int c = count - (i + 1);
150
151                System.arraycopy(spans, i + 1, spans, i, c);
152                System.arraycopy(data, (i + 1) * COLUMNS,
153                                 data, i * COLUMNS, c * COLUMNS);
154
155                mSpanCount--;
156
157                sendSpanRemoved(what, ostart, oend);
158                return;
159            }
160        }
161    }
162
163    public int getSpanStart(Object what) {
164        int count = mSpanCount;
165        Object[] spans = mSpans;
166        int[] data = mSpanData;
167
168        for (int i = count - 1; i >= 0; i--) {
169            if (spans[i] == what) {
170                return data[i * COLUMNS + START];
171            }
172        }
173
174        return -1;
175    }
176
177    public int getSpanEnd(Object what) {
178        int count = mSpanCount;
179        Object[] spans = mSpans;
180        int[] data = mSpanData;
181
182        for (int i = count - 1; i >= 0; i--) {
183            if (spans[i] == what) {
184                return data[i * COLUMNS + END];
185            }
186        }
187
188        return -1;
189    }
190
191    public int getSpanFlags(Object what) {
192        int count = mSpanCount;
193        Object[] spans = mSpans;
194        int[] data = mSpanData;
195
196        for (int i = count - 1; i >= 0; i--) {
197            if (spans[i] == what) {
198                return data[i * COLUMNS + FLAGS];
199            }
200        }
201
202        return 0;
203    }
204
205    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
206        int count = 0;
207
208        int spanCount = mSpanCount;
209        Object[] spans = mSpans;
210        int[] data = mSpanData;
211        Object[] ret = null;
212        Object ret1 = null;
213
214        for (int i = 0; i < spanCount; i++) {
215            if (kind != null && !kind.isInstance(spans[i])) {
216                continue;
217            }
218
219            int spanStart = data[i * COLUMNS + START];
220            int spanEnd = data[i * COLUMNS + END];
221
222            if (spanStart > queryEnd) {
223                continue;
224            }
225            if (spanEnd < queryStart) {
226                continue;
227            }
228
229            if (spanStart != spanEnd && queryStart != queryEnd) {
230                if (spanStart == queryEnd) {
231                    continue;
232                }
233                if (spanEnd == queryStart) {
234                    continue;
235                }
236            }
237
238            if (count == 0) {
239                ret1 = spans[i];
240                count++;
241            } else {
242                if (count == 1) {
243                    ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
244                    ret[0] = ret1;
245                }
246
247                int prio = data[i * COLUMNS + FLAGS] & Spanned.SPAN_PRIORITY;
248                if (prio != 0) {
249                    int j;
250
251                    for (j = 0; j < count; j++) {
252                        int p = getSpanFlags(ret[j]) & Spanned.SPAN_PRIORITY;
253
254                        if (prio > p) {
255                            break;
256                        }
257                    }
258
259                    System.arraycopy(ret, j, ret, j + 1, count - j);
260                    ret[j] = spans[i];
261                    count++;
262                } else {
263                    ret[count++] = spans[i];
264                }
265            }
266        }
267
268        if (count == 0) {
269            return (T[]) ArrayUtils.emptyArray(kind);
270        }
271        if (count == 1) {
272            ret = (Object[]) Array.newInstance(kind, 1);
273            ret[0] = ret1;
274            return (T[]) ret;
275        }
276        if (count == ret.length) {
277            return (T[]) ret;
278        }
279
280        Object[] nret = (Object[]) Array.newInstance(kind, count);
281        System.arraycopy(ret, 0, nret, 0, count);
282        return (T[]) nret;
283    }
284
285    public int nextSpanTransition(int start, int limit, Class kind) {
286        int count = mSpanCount;
287        Object[] spans = mSpans;
288        int[] data = mSpanData;
289
290        if (kind == null) {
291            kind = Object.class;
292        }
293
294        for (int i = 0; i < count; i++) {
295            int st = data[i * COLUMNS + START];
296            int en = data[i * COLUMNS + END];
297
298            if (st > start && st < limit && kind.isInstance(spans[i]))
299                limit = st;
300            if (en > start && en < limit && kind.isInstance(spans[i]))
301                limit = en;
302        }
303
304        return limit;
305    }
306
307    private void sendSpanAdded(Object what, int start, int end) {
308        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
309        int n = recip.length;
310
311        for (int i = 0; i < n; i++) {
312            recip[i].onSpanAdded((Spannable) this, what, start, end);
313        }
314    }
315
316    private void sendSpanRemoved(Object what, int start, int end) {
317        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
318        int n = recip.length;
319
320        for (int i = 0; i < n; i++) {
321            recip[i].onSpanRemoved((Spannable) this, what, start, end);
322        }
323    }
324
325    private void sendSpanChanged(Object what, int s, int e, int st, int en) {
326        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
327                                       SpanWatcher.class);
328        int n = recip.length;
329
330        for (int i = 0; i < n; i++) {
331            recip[i].onSpanChanged((Spannable) this, what, s, e, st, en);
332        }
333    }
334
335    private static String region(int start, int end) {
336        return "(" + start + " ... " + end + ")";
337    }
338
339    private void checkRange(final String operation, int start, int end) {
340        if (end < start) {
341            throw new IndexOutOfBoundsException(operation + " " +
342                                                region(start, end) +
343                                                " has end before start");
344        }
345
346        int len = length();
347
348        if (start > len || end > len) {
349            throw new IndexOutOfBoundsException(operation + " " +
350                                                region(start, end) +
351                                                " ends beyond length " + len);
352        }
353
354        if (start < 0 || end < 0) {
355            throw new IndexOutOfBoundsException(operation + " " +
356                                                region(start, end) +
357                                                " starts before 0");
358        }
359    }
360
361    private String mText;
362    private Object[] mSpans;
363    private int[] mSpanData;
364    private int mSpanCount;
365
366    /* package */ static final Object[] EMPTY = new Object[0];
367
368    private static final int START = 0;
369    private static final int END = 1;
370    private static final int FLAGS = 2;
371    private static final int COLUMNS = 3;
372}
373