Home | History | Annotate | Download | only in text
      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 {
     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     // Same as SpannableStringBuilder
    362     @Override
    363     public boolean equals(Object o) {
    364         if (o instanceof Spanned &&
    365                 toString().equals(o.toString())) {
    366             Spanned other = (Spanned) o;
    367             // Check span data
    368             Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
    369             if (mSpanCount == otherSpans.length) {
    370                 for (int i = 0; i < mSpanCount; ++i) {
    371                     Object thisSpan = mSpans[i];
    372                     Object otherSpan = otherSpans[i];
    373                     if (thisSpan == this) {
    374                         if (other != otherSpan ||
    375                                 getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
    376                                 getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
    377                                 getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
    378                             return false;
    379                         }
    380                     } else if (!thisSpan.equals(otherSpan) ||
    381                             getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
    382                             getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
    383                             getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
    384                         return false;
    385                     }
    386                 }
    387                 return true;
    388             }
    389         }
    390         return false;
    391     }
    392 
    393     // Same as SpannableStringBuilder
    394     @Override
    395     public int hashCode() {
    396         int hash = toString().hashCode();
    397         hash = hash * 31 + mSpanCount;
    398         for (int i = 0; i < mSpanCount; ++i) {
    399             Object span = mSpans[i];
    400             if (span != this) {
    401                 hash = hash * 31 + span.hashCode();
    402             }
    403             hash = hash * 31 + getSpanStart(span);
    404             hash = hash * 31 + getSpanEnd(span);
    405             hash = hash * 31 + getSpanFlags(span);
    406         }
    407         return hash;
    408     }
    409 
    410     private String mText;
    411     private Object[] mSpans;
    412     private int[] mSpanData;
    413     private int mSpanCount;
    414 
    415     /* package */ static final Object[] EMPTY = new Object[0];
    416 
    417     private static final int START = 0;
    418     private static final int END = 1;
    419     private static final int FLAGS = 2;
    420     private static final int COLUMNS = 3;
    421 }
    422