• 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 import com.android.internal.util.GrowingArrayUtils;
21 
22 import libcore.util.EmptyArray;
23 
24 import java.lang.reflect.Array;
25 
26 /* package */ abstract class SpannableStringInternal
27 {
SpannableStringInternal(CharSequence source, int start, int end)28     /* package */ SpannableStringInternal(CharSequence source,
29                                           int start, int end) {
30         if (start == 0 && end == source.length())
31             mText = source.toString();
32         else
33             mText = source.toString().substring(start, end);
34 
35         mSpans = EmptyArray.OBJECT;
36         mSpanData = EmptyArray.INT;
37 
38         if (source instanceof Spanned) {
39             Spanned sp = (Spanned) source;
40             Object[] spans = sp.getSpans(start, end, Object.class);
41 
42             for (int i = 0; i < spans.length; i++) {
43                 int st = sp.getSpanStart(spans[i]);
44                 int en = sp.getSpanEnd(spans[i]);
45                 int fl = sp.getSpanFlags(spans[i]);
46 
47                 if (st < start)
48                     st = start;
49                 if (en > end)
50                     en = end;
51 
52                 setSpan(spans[i], st - start, en - start, fl);
53             }
54         }
55     }
56 
length()57     public final int length() {
58         return mText.length();
59     }
60 
charAt(int i)61     public final char charAt(int i) {
62         return mText.charAt(i);
63     }
64 
toString()65     public final String toString() {
66         return mText;
67     }
68 
69     /* subclasses must do subSequence() to preserve type */
70 
getChars(int start, int end, char[] dest, int off)71     public final void getChars(int start, int end, char[] dest, int off) {
72         mText.getChars(start, end, dest, off);
73     }
74 
setSpan(Object what, int start, int end, int flags)75     /* package */ void setSpan(Object what, int start, int end, int flags) {
76         int nstart = start;
77         int nend = end;
78 
79         checkRange("setSpan", start, end);
80 
81         if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) {
82             if (start != 0 && start != length()) {
83                 char c = charAt(start - 1);
84 
85                 if (c != '\n')
86                     throw new RuntimeException(
87                             "PARAGRAPH span must start at paragraph boundary" +
88                             " (" + start + " follows " + c + ")");
89             }
90 
91             if (end != 0 && end != length()) {
92                 char c = charAt(end - 1);
93 
94                 if (c != '\n')
95                     throw new RuntimeException(
96                             "PARAGRAPH span must end at paragraph boundary" +
97                             " (" + end + " follows " + c + ")");
98             }
99         }
100 
101         int count = mSpanCount;
102         Object[] spans = mSpans;
103         int[] data = mSpanData;
104 
105         for (int i = 0; i < count; i++) {
106             if (spans[i] == what) {
107                 int ostart = data[i * COLUMNS + START];
108                 int oend = data[i * COLUMNS + END];
109 
110                 data[i * COLUMNS + START] = start;
111                 data[i * COLUMNS + END] = end;
112                 data[i * COLUMNS + FLAGS] = flags;
113 
114                 sendSpanChanged(what, ostart, oend, nstart, nend);
115                 return;
116             }
117         }
118 
119         if (mSpanCount + 1 >= mSpans.length) {
120             Object[] newtags = ArrayUtils.newUnpaddedObjectArray(
121                     GrowingArrayUtils.growSize(mSpanCount));
122             int[] newdata = new int[newtags.length * 3];
123 
124             System.arraycopy(mSpans, 0, newtags, 0, mSpanCount);
125             System.arraycopy(mSpanData, 0, newdata, 0, mSpanCount * 3);
126 
127             mSpans = newtags;
128             mSpanData = newdata;
129         }
130 
131         mSpans[mSpanCount] = what;
132         mSpanData[mSpanCount * COLUMNS + START] = start;
133         mSpanData[mSpanCount * COLUMNS + END] = end;
134         mSpanData[mSpanCount * COLUMNS + FLAGS] = flags;
135         mSpanCount++;
136 
137         if (this instanceof Spannable)
138             sendSpanAdded(what, nstart, nend);
139     }
140 
removeSpan(Object what)141     /* package */ void removeSpan(Object what) {
142         int count = mSpanCount;
143         Object[] spans = mSpans;
144         int[] data = mSpanData;
145 
146         for (int i = count - 1; i >= 0; i--) {
147             if (spans[i] == what) {
148                 int ostart = data[i * COLUMNS + START];
149                 int oend = data[i * COLUMNS + END];
150 
151                 int c = count - (i + 1);
152 
153                 System.arraycopy(spans, i + 1, spans, i, c);
154                 System.arraycopy(data, (i + 1) * COLUMNS,
155                                  data, i * COLUMNS, c * COLUMNS);
156 
157                 mSpanCount--;
158 
159                 sendSpanRemoved(what, ostart, oend);
160                 return;
161             }
162         }
163     }
164 
getSpanStart(Object what)165     public int getSpanStart(Object what) {
166         int count = mSpanCount;
167         Object[] spans = mSpans;
168         int[] data = mSpanData;
169 
170         for (int i = count - 1; i >= 0; i--) {
171             if (spans[i] == what) {
172                 return data[i * COLUMNS + START];
173             }
174         }
175 
176         return -1;
177     }
178 
getSpanEnd(Object what)179     public int getSpanEnd(Object what) {
180         int count = mSpanCount;
181         Object[] spans = mSpans;
182         int[] data = mSpanData;
183 
184         for (int i = count - 1; i >= 0; i--) {
185             if (spans[i] == what) {
186                 return data[i * COLUMNS + END];
187             }
188         }
189 
190         return -1;
191     }
192 
getSpanFlags(Object what)193     public int getSpanFlags(Object what) {
194         int count = mSpanCount;
195         Object[] spans = mSpans;
196         int[] data = mSpanData;
197 
198         for (int i = count - 1; i >= 0; i--) {
199             if (spans[i] == what) {
200                 return data[i * COLUMNS + FLAGS];
201             }
202         }
203 
204         return 0;
205     }
206 
getSpans(int queryStart, int queryEnd, Class<T> kind)207     public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
208         int count = 0;
209 
210         int spanCount = mSpanCount;
211         Object[] spans = mSpans;
212         int[] data = mSpanData;
213         Object[] ret = null;
214         Object ret1 = null;
215 
216         for (int i = 0; i < spanCount; i++) {
217             if (kind != null && !kind.isInstance(spans[i])) {
218                 continue;
219             }
220 
221             int spanStart = data[i * COLUMNS + START];
222             int spanEnd = data[i * COLUMNS + END];
223 
224             if (spanStart > queryEnd) {
225                 continue;
226             }
227             if (spanEnd < queryStart) {
228                 continue;
229             }
230 
231             if (spanStart != spanEnd && queryStart != queryEnd) {
232                 if (spanStart == queryEnd) {
233                     continue;
234                 }
235                 if (spanEnd == queryStart) {
236                     continue;
237                 }
238             }
239 
240             if (count == 0) {
241                 ret1 = spans[i];
242                 count++;
243             } else {
244                 if (count == 1) {
245                     ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
246                     ret[0] = ret1;
247                 }
248 
249                 int prio = data[i * COLUMNS + FLAGS] & Spanned.SPAN_PRIORITY;
250                 if (prio != 0) {
251                     int j;
252 
253                     for (j = 0; j < count; j++) {
254                         int p = getSpanFlags(ret[j]) & Spanned.SPAN_PRIORITY;
255 
256                         if (prio > p) {
257                             break;
258                         }
259                     }
260 
261                     System.arraycopy(ret, j, ret, j + 1, count - j);
262                     ret[j] = spans[i];
263                     count++;
264                 } else {
265                     ret[count++] = spans[i];
266                 }
267             }
268         }
269 
270         if (count == 0) {
271             return (T[]) ArrayUtils.emptyArray(kind);
272         }
273         if (count == 1) {
274             ret = (Object[]) Array.newInstance(kind, 1);
275             ret[0] = ret1;
276             return (T[]) ret;
277         }
278         if (count == ret.length) {
279             return (T[]) ret;
280         }
281 
282         Object[] nret = (Object[]) Array.newInstance(kind, count);
283         System.arraycopy(ret, 0, nret, 0, count);
284         return (T[]) nret;
285     }
286 
nextSpanTransition(int start, int limit, Class kind)287     public int nextSpanTransition(int start, int limit, Class kind) {
288         int count = mSpanCount;
289         Object[] spans = mSpans;
290         int[] data = mSpanData;
291 
292         if (kind == null) {
293             kind = Object.class;
294         }
295 
296         for (int i = 0; i < count; i++) {
297             int st = data[i * COLUMNS + START];
298             int en = data[i * COLUMNS + END];
299 
300             if (st > start && st < limit && kind.isInstance(spans[i]))
301                 limit = st;
302             if (en > start && en < limit && kind.isInstance(spans[i]))
303                 limit = en;
304         }
305 
306         return limit;
307     }
308 
sendSpanAdded(Object what, int start, int end)309     private void sendSpanAdded(Object what, int start, int end) {
310         SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
311         int n = recip.length;
312 
313         for (int i = 0; i < n; i++) {
314             recip[i].onSpanAdded((Spannable) this, what, start, end);
315         }
316     }
317 
sendSpanRemoved(Object what, int start, int end)318     private void sendSpanRemoved(Object what, int start, int end) {
319         SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
320         int n = recip.length;
321 
322         for (int i = 0; i < n; i++) {
323             recip[i].onSpanRemoved((Spannable) this, what, start, end);
324         }
325     }
326 
sendSpanChanged(Object what, int s, int e, int st, int en)327     private void sendSpanChanged(Object what, int s, int e, int st, int en) {
328         SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
329                                        SpanWatcher.class);
330         int n = recip.length;
331 
332         for (int i = 0; i < n; i++) {
333             recip[i].onSpanChanged((Spannable) this, what, s, e, st, en);
334         }
335     }
336 
region(int start, int end)337     private static String region(int start, int end) {
338         return "(" + start + " ... " + end + ")";
339     }
340 
checkRange(final String operation, int start, int end)341     private void checkRange(final String operation, int start, int end) {
342         if (end < start) {
343             throw new IndexOutOfBoundsException(operation + " " +
344                                                 region(start, end) +
345                                                 " has end before start");
346         }
347 
348         int len = length();
349 
350         if (start > len || end > len) {
351             throw new IndexOutOfBoundsException(operation + " " +
352                                                 region(start, end) +
353                                                 " ends beyond length " + len);
354         }
355 
356         if (start < 0 || end < 0) {
357             throw new IndexOutOfBoundsException(operation + " " +
358                                                 region(start, end) +
359                                                 " starts before 0");
360         }
361     }
362 
363     // Same as SpannableStringBuilder
364     @Override
equals(Object o)365     public boolean equals(Object o) {
366         if (o instanceof Spanned &&
367                 toString().equals(o.toString())) {
368             Spanned other = (Spanned) o;
369             // Check span data
370             Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
371             if (mSpanCount == otherSpans.length) {
372                 for (int i = 0; i < mSpanCount; ++i) {
373                     Object thisSpan = mSpans[i];
374                     Object otherSpan = otherSpans[i];
375                     if (thisSpan == this) {
376                         if (other != otherSpan ||
377                                 getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
378                                 getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
379                                 getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
380                             return false;
381                         }
382                     } else if (!thisSpan.equals(otherSpan) ||
383                             getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
384                             getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
385                             getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
386                         return false;
387                     }
388                 }
389                 return true;
390             }
391         }
392         return false;
393     }
394 
395     // Same as SpannableStringBuilder
396     @Override
hashCode()397     public int hashCode() {
398         int hash = toString().hashCode();
399         hash = hash * 31 + mSpanCount;
400         for (int i = 0; i < mSpanCount; ++i) {
401             Object span = mSpans[i];
402             if (span != this) {
403                 hash = hash * 31 + span.hashCode();
404             }
405             hash = hash * 31 + getSpanStart(span);
406             hash = hash * 31 + getSpanEnd(span);
407             hash = hash * 31 + getSpanFlags(span);
408         }
409         return hash;
410     }
411 
412     private String mText;
413     private Object[] mSpans;
414     private int[] mSpanData;
415     private int mSpanCount;
416 
417     /* package */ static final Object[] EMPTY = new Object[0];
418 
419     private static final int START = 0;
420     private static final int END = 1;
421     private static final int FLAGS = 2;
422     private static final int COLUMNS = 3;
423 }
424