• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.text;
18 
19 import com.android.internal.util.ArrayUtils;
20 
21 import java.lang.reflect.Array;
22 
23 /* package */ abstract class SpannableStringInternal
24 {
SpannableStringInternal(CharSequence source, int start, int end)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 
length()55     public final int length() {
56         return mText.length();
57     }
58 
charAt(int i)59     public final char charAt(int i) {
60         return mText.charAt(i);
61     }
62 
toString()63     public final String toString() {
64         return mText;
65     }
66 
67     /* subclasses must do subSequence() to preserve type */
68 
getChars(int start, int end, char[] dest, int off)69     public final void getChars(int start, int end, char[] dest, int off) {
70         mText.getChars(start, end, dest, off);
71     }
72 
setSpan(Object what, int start, int end, int flags)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 
removeSpan(Object what)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 
getSpanStart(Object what)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 
getSpanEnd(Object what)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 
getSpanFlags(Object what)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 
getSpans(int queryStart, int queryEnd, Class<T> kind)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 
nextSpanTransition(int start, int limit, Class kind)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 
sendSpanAdded(Object what, int start, int end)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 
sendSpanRemoved(Object what, int start, int end)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 
sendSpanChanged(Object what, int s, int e, int st, int en)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 
region(int start, int end)335     private static String region(int start, int end) {
336         return "(" + start + " ... " + end + ")";
337     }
338 
checkRange(final String operation, int start, int end)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