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             int spanStart = data[i * COLUMNS + START];
218             int spanEnd = data[i * COLUMNS + END];
219 
220             if (spanStart > queryEnd) {
221                 continue;
222             }
223             if (spanEnd < queryStart) {
224                 continue;
225             }
226 
227             if (spanStart != spanEnd && queryStart != queryEnd) {
228                 if (spanStart == queryEnd) {
229                     continue;
230                 }
231                 if (spanEnd == queryStart) {
232                     continue;
233                 }
234             }
235 
236             // verify span class as late as possible, since it is expensive
237             if (kind != null && !kind.isInstance(spans[i])) {
238                 continue;
239             }
240 
241             if (count == 0) {
242                 ret1 = spans[i];
243                 count++;
244             } else {
245                 if (count == 1) {
246                     ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
247                     ret[0] = ret1;
248                 }
249 
250                 int prio = data[i * COLUMNS + FLAGS] & Spanned.SPAN_PRIORITY;
251                 if (prio != 0) {
252                     int j;
253 
254                     for (j = 0; j < count; j++) {
255                         int p = getSpanFlags(ret[j]) & Spanned.SPAN_PRIORITY;
256 
257                         if (prio > p) {
258                             break;
259                         }
260                     }
261 
262                     System.arraycopy(ret, j, ret, j + 1, count - j);
263                     ret[j] = spans[i];
264                     count++;
265                 } else {
266                     ret[count++] = spans[i];
267                 }
268             }
269         }
270 
271         if (count == 0) {
272             return (T[]) ArrayUtils.emptyArray(kind);
273         }
274         if (count == 1) {
275             ret = (Object[]) Array.newInstance(kind, 1);
276             ret[0] = ret1;
277             return (T[]) ret;
278         }
279         if (count == ret.length) {
280             return (T[]) ret;
281         }
282 
283         Object[] nret = (Object[]) Array.newInstance(kind, count);
284         System.arraycopy(ret, 0, nret, 0, count);
285         return (T[]) nret;
286     }
287 
nextSpanTransition(int start, int limit, Class kind)288     public int nextSpanTransition(int start, int limit, Class kind) {
289         int count = mSpanCount;
290         Object[] spans = mSpans;
291         int[] data = mSpanData;
292 
293         if (kind == null) {
294             kind = Object.class;
295         }
296 
297         for (int i = 0; i < count; i++) {
298             int st = data[i * COLUMNS + START];
299             int en = data[i * COLUMNS + END];
300 
301             if (st > start && st < limit && kind.isInstance(spans[i]))
302                 limit = st;
303             if (en > start && en < limit && kind.isInstance(spans[i]))
304                 limit = en;
305         }
306 
307         return limit;
308     }
309 
sendSpanAdded(Object what, int start, int end)310     private void sendSpanAdded(Object what, int start, int end) {
311         SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
312         int n = recip.length;
313 
314         for (int i = 0; i < n; i++) {
315             recip[i].onSpanAdded((Spannable) this, what, start, end);
316         }
317     }
318 
sendSpanRemoved(Object what, int start, int end)319     private void sendSpanRemoved(Object what, int start, int end) {
320         SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
321         int n = recip.length;
322 
323         for (int i = 0; i < n; i++) {
324             recip[i].onSpanRemoved((Spannable) this, what, start, end);
325         }
326     }
327 
sendSpanChanged(Object what, int s, int e, int st, int en)328     private void sendSpanChanged(Object what, int s, int e, int st, int en) {
329         SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
330                                        SpanWatcher.class);
331         int n = recip.length;
332 
333         for (int i = 0; i < n; i++) {
334             recip[i].onSpanChanged((Spannable) this, what, s, e, st, en);
335         }
336     }
337 
region(int start, int end)338     private static String region(int start, int end) {
339         return "(" + start + " ... " + end + ")";
340     }
341 
checkRange(final String operation, int start, int end)342     private void checkRange(final String operation, int start, int end) {
343         if (end < start) {
344             throw new IndexOutOfBoundsException(operation + " " +
345                                                 region(start, end) +
346                                                 " has end before start");
347         }
348 
349         int len = length();
350 
351         if (start > len || end > len) {
352             throw new IndexOutOfBoundsException(operation + " " +
353                                                 region(start, end) +
354                                                 " ends beyond length " + len);
355         }
356 
357         if (start < 0 || end < 0) {
358             throw new IndexOutOfBoundsException(operation + " " +
359                                                 region(start, end) +
360                                                 " starts before 0");
361         }
362     }
363 
364     // Same as SpannableStringBuilder
365     @Override
equals(Object o)366     public boolean equals(Object o) {
367         if (o instanceof Spanned &&
368                 toString().equals(o.toString())) {
369             Spanned other = (Spanned) o;
370             // Check span data
371             Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
372             if (mSpanCount == otherSpans.length) {
373                 for (int i = 0; i < mSpanCount; ++i) {
374                     Object thisSpan = mSpans[i];
375                     Object otherSpan = otherSpans[i];
376                     if (thisSpan == this) {
377                         if (other != otherSpan ||
378                                 getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
379                                 getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
380                                 getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
381                             return false;
382                         }
383                     } else if (!thisSpan.equals(otherSpan) ||
384                             getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
385                             getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
386                             getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
387                         return false;
388                     }
389                 }
390                 return true;
391             }
392         }
393         return false;
394     }
395 
396     // Same as SpannableStringBuilder
397     @Override
hashCode()398     public int hashCode() {
399         int hash = toString().hashCode();
400         hash = hash * 31 + mSpanCount;
401         for (int i = 0; i < mSpanCount; ++i) {
402             Object span = mSpans[i];
403             if (span != this) {
404                 hash = hash * 31 + span.hashCode();
405             }
406             hash = hash * 31 + getSpanStart(span);
407             hash = hash * 31 + getSpanEnd(span);
408             hash = hash * 31 + getSpanFlags(span);
409         }
410         return hash;
411     }
412 
413     private String mText;
414     private Object[] mSpans;
415     private int[] mSpanData;
416     private int mSpanCount;
417 
418     /* package */ static final Object[] EMPTY = new Object[0];
419 
420     private static final int START = 0;
421     private static final int END = 1;
422     private static final int FLAGS = 2;
423     private static final int COLUMNS = 3;
424 }
425