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 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 {
     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 
     57     public final int length() {
     58         return mText.length();
     59     }
     60 
     61     public final char charAt(int i) {
     62         return mText.charAt(i);
     63     }
     64 
     65     public final String toString() {
     66         return mText;
     67     }
     68 
     69     /* subclasses must do subSequence() to preserve type */
     70 
     71     public final void getChars(int start, int end, char[] dest, int off) {
     72         mText.getChars(start, end, dest, off);
     73     }
     74 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    338     private static String region(int start, int end) {
    339         return "(" + start + " ... " + end + ")";
    340     }
    341 
    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
    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
    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