Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 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.cts;
     18 
     19 
     20 import static android.view.View.LAYOUT_DIRECTION_LTR;
     21 import static android.view.View.LAYOUT_DIRECTION_RTL;
     22 
     23 import android.content.res.ColorStateList;
     24 import android.graphics.Canvas;
     25 import android.graphics.Color;
     26 import android.graphics.Paint;
     27 import android.graphics.Paint.FontMetricsInt;
     28 import android.graphics.Typeface;
     29 import android.os.Parcel;
     30 import android.os.Parcelable;
     31 import android.test.AndroidTestCase;
     32 import android.text.GetChars;
     33 import android.text.SpannableString;
     34 import android.text.SpannableStringBuilder;
     35 import android.text.Spanned;
     36 import android.text.SpannedString;
     37 import android.text.TextPaint;
     38 import android.text.TextUtils;
     39 import android.text.TextUtils.TruncateAt;
     40 import android.text.style.BackgroundColorSpan;
     41 import android.text.style.ReplacementSpan;
     42 import android.text.style.TextAppearanceSpan;
     43 import android.text.style.URLSpan;
     44 import android.util.Log;
     45 import android.util.StringBuilderPrinter;
     46 
     47 import dalvik.annotation.TestLevel;
     48 import dalvik.annotation.TestTargetNew;
     49 
     50 import java.util.ArrayList;
     51 import java.util.Locale;
     52 import java.util.regex.Pattern;
     53 
     54 /**
     55  * Test {@link TextUtils}.
     56  */
     57 public class TextUtilsTest extends AndroidTestCase {
     58     private static String mEllipsis;
     59     private int mStart;
     60     private int mEnd;
     61 
     62     @Override
     63     protected void setUp() throws Exception {
     64         super.setUp();
     65         mEllipsis = getEllipsis();
     66         resetRange();
     67     }
     68 
     69     private void resetRange() {
     70         mStart = -1;
     71         mEnd = -1;
     72     }
     73 
     74     /**
     75      * Get the ellipsis from system.
     76      * @return the string of ellipsis.
     77      */
     78     private String getEllipsis() {
     79         String text = "xxxxx";
     80         TextPaint p = new TextPaint();
     81         float width = p.measureText(text.substring(1));
     82         String re = TextUtils.ellipsize(text, p, width, TruncateAt.START).toString();
     83         return re.substring(0, re.indexOf("x"));
     84     }
     85 
     86     public void testCommaEllipsize() {
     87         TextPaint p = new TextPaint();
     88         String text = "long, string, to, truncate";
     89 
     90         float textWidth = p.measureText("long, 3 plus");
     91         // avail is shorter than text width for only one item plus the appropriate ellipsis.
     92         // issue 1688347, the expected result for this case does not be described
     93         // in the javadoc of commaEllipsize().
     94         assertEquals("",
     95                 TextUtils.commaEllipsize(text, p, textWidth - 1.4f, "plus 1", "%d plus").toString());
     96         // avail is long enough for only one item plus the appropriate ellipsis.
     97         assertEquals("long, 3 plus",
     98                 TextUtils.commaEllipsize(text, p, textWidth, "plus 1", "%d plus").toString());
     99 
    100         // avail is long enough for two item plus the appropriate ellipsis.
    101         textWidth = p.measureText("long, string, 2 more");
    102         assertEquals("long, string, 2 more",
    103                 TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString());
    104 
    105         // avail is long enough for the whole sentence.
    106         textWidth = p.measureText("long, string, to, truncate");
    107         assertEquals("long, string, to, truncate",
    108                 TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString());
    109 
    110         // the sentence is extended, avail is NOT long enough for the whole sentence.
    111         assertEquals("long, string, to, more 1", TextUtils.commaEllipsize(
    112                 text + "-extended", p, textWidth, "more 1", "%d more").toString());
    113 
    114         // exceptional value
    115         assertEquals("", TextUtils.commaEllipsize(text, p, -1f, "plus 1", "%d plus").toString());
    116 
    117         assertEquals(text, TextUtils.commaEllipsize(
    118                 text, p, Float.MAX_VALUE, "more 1", "%d more").toString());
    119 
    120         assertEquals("long, string, to, null", TextUtils.commaEllipsize(
    121                 text + "-extended", p, textWidth, null, "%d more").toString());
    122 
    123         try {
    124             TextUtils.commaEllipsize(null, p, textWidth, "plus 1", "%d plus");
    125             fail("Should throw NullPointerException");
    126         } catch (NullPointerException e) {
    127             // issue 1688347, not clear what is supposed to happen if the text to truncate is null.
    128         }
    129 
    130         try {
    131             TextUtils.commaEllipsize(text, null, textWidth, "plus 1", "%d plus");
    132             fail("Should throw NullPointerException");
    133         } catch (NullPointerException e) {
    134             // issue 1688347, not clear what is supposed to happen if TextPaint is null.
    135         }
    136     }
    137 
    138     public void testConcat() {
    139         // issue 1695243
    140         // the javadoc for concat() doesn't describe the expected result when parameter is empty.
    141         assertEquals("", TextUtils.concat().toString());
    142 
    143         assertEquals("first", TextUtils.concat("first").toString());
    144 
    145         assertEquals("first, second", TextUtils.concat("first", ", ", "second").toString());
    146 
    147         SpannableString string1 = new SpannableString("first");
    148         SpannableString string2 = new SpannableString("second");
    149         final String url = "www.test_url.com";
    150         URLSpan urlSpan = new URLSpan(url);
    151         string1.setSpan(urlSpan, 0, string1.length() - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
    152         BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
    153         string2.setSpan(bgColorSpan, 0, string2.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    154 
    155         final String comma = ", ";
    156         Spanned strResult = (Spanned) TextUtils.concat(string1, comma, string2);
    157         assertEquals(string1.toString() + comma + string2.toString(), strResult.toString());
    158         Object spans[] = strResult.getSpans(0, strResult.length(), Object.class);
    159         assertEquals(2, spans.length);
    160         assertTrue(spans[0] instanceof URLSpan);
    161         assertEquals(url, ((URLSpan) spans[0]).getURL());
    162         assertTrue(spans[1] instanceof BackgroundColorSpan);
    163         assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor());
    164         assertEquals(0, strResult.getSpanStart(urlSpan));
    165         assertEquals(string1.length() - 1, strResult.getSpanEnd(urlSpan));
    166         assertEquals(string1.length() + comma.length(), strResult.getSpanStart(bgColorSpan));
    167         assertEquals(strResult.length() - 1, strResult.getSpanEnd(bgColorSpan));
    168 
    169         assertEquals(string1, TextUtils.concat(string1));
    170 
    171         // issue 1695243, the javadoc for concat() doesn't describe
    172         // the expected result when parameters are null.
    173         assertEquals(null, TextUtils.concat((CharSequence) null));
    174 
    175         try {
    176             TextUtils.concat((CharSequence[]) null);
    177             fail("Should throw NullPointerException");
    178         } catch (NullPointerException e) {
    179             // expected
    180         }
    181     }
    182 
    183     public void testCopySpansFrom() {
    184         Object[] spans;
    185         String text = "content";
    186         SpannableString source1 = new SpannableString(text);
    187         int midPos = source1.length() / 2;
    188         final String url = "www.test_url.com";
    189         URLSpan urlSpan = new URLSpan(url);
    190         source1.setSpan(urlSpan, 0, midPos, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
    191         BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
    192         source1.setSpan(bgColorSpan, midPos - 1,
    193                 source1.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    194 
    195         // normal test
    196         SpannableString dest1 = new SpannableString(text);
    197         TextUtils.copySpansFrom(source1, 0, source1.length(), Object.class, dest1, 0);
    198         spans = dest1.getSpans(0, dest1.length(), Object.class);
    199         assertEquals(2, spans.length);
    200         assertTrue(spans[0] instanceof URLSpan);
    201         assertEquals(url, ((URLSpan) spans[0]).getURL());
    202         assertTrue(spans[1] instanceof BackgroundColorSpan);
    203         assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor());
    204         assertEquals(0, dest1.getSpanStart(urlSpan));
    205         assertEquals(midPos, dest1.getSpanEnd(urlSpan));
    206         assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, dest1.getSpanFlags(urlSpan));
    207         assertEquals(midPos - 1, dest1.getSpanStart(bgColorSpan));
    208         assertEquals(source1.length() - 1, dest1.getSpanEnd(bgColorSpan));
    209         assertEquals(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, dest1.getSpanFlags(bgColorSpan));
    210 
    211         SpannableString source2 = new SpannableString(text);
    212         source2.setSpan(urlSpan, 0, source2.length() - 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    213         SpannableString dest2 = new SpannableString(text);
    214         TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest2, 0);
    215         spans = dest2.getSpans(0, dest2.length(), Object.class);
    216         assertEquals(1, spans.length);
    217         assertTrue(spans[0] instanceof URLSpan);
    218         assertEquals(url, ((URLSpan) spans[0]).getURL());
    219         assertEquals(0, dest2.getSpanStart(urlSpan));
    220         assertEquals(source2.length() - 1, dest2.getSpanEnd(urlSpan));
    221         assertEquals(Spanned.SPAN_EXCLUSIVE_INCLUSIVE, dest2.getSpanFlags(urlSpan));
    222 
    223         SpannableString dest3 = new SpannableString(text);
    224         TextUtils.copySpansFrom(source2, 0, source2.length(), BackgroundColorSpan.class, dest3, 0);
    225         spans = dest3.getSpans(0, dest3.length(), Object.class);
    226         assertEquals(0, spans.length);
    227         TextUtils.copySpansFrom(source2, 0, source2.length(), URLSpan.class, dest3, 0);
    228         spans = dest3.getSpans(0, dest3.length(), Object.class);
    229         assertEquals(1, spans.length);
    230 
    231         SpannableString dest4 = new SpannableString("short");
    232         try {
    233             TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest4, 0);
    234             fail("Should throw IndexOutOfBoundsException");
    235         } catch (IndexOutOfBoundsException e) {
    236             // expected
    237         }
    238         TextUtils.copySpansFrom(source2, 0, dest4.length(), Object.class, dest4, 0);
    239         spans = dest4.getSpans(0, dest4.length(), Object.class);
    240         assertEquals(1, spans.length);
    241         assertEquals(0, dest4.getSpanStart(spans[0]));
    242         // issue 1688347, not clear the expected result when 'start ~ end' only
    243         // covered a part of the span.
    244         assertEquals(dest4.length(), dest4.getSpanEnd(spans[0]));
    245 
    246         SpannableString dest5 = new SpannableString("longer content");
    247         TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 0);
    248         spans = dest5.getSpans(0, 1, Object.class);
    249         assertEquals(1, spans.length);
    250 
    251         dest5 = new SpannableString("longer content");
    252         TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 2);
    253         spans = dest5.getSpans(0, 1, Object.class);
    254         assertEquals(0, spans.length);
    255         spans = dest5.getSpans(2, dest5.length(), Object.class);
    256         assertEquals(1, spans.length);
    257         try {
    258             TextUtils.copySpansFrom(source2, 0, source2.length(),
    259                     Object.class, dest5, dest5.length() - source2.length() + 2);
    260             fail("Should throw IndexOutOfBoundsException");
    261         } catch (IndexOutOfBoundsException e) {
    262             // expected
    263         }
    264 
    265         // issue 1688347, no javadoc about the expected behavior of the exceptional argument.
    266         // exceptional source start
    267         SpannableString dest6 = new SpannableString("exceptional test");
    268         TextUtils.copySpansFrom(source2, -1, source2.length(), Object.class, dest6, 0);
    269         spans = dest6.getSpans(0, dest6.length(), Object.class);
    270         assertEquals(1, spans.length);
    271         dest6 = new SpannableString("exceptional test");
    272         TextUtils.copySpansFrom(source2, Integer.MAX_VALUE, source2.length() - 1,
    273                     Object.class, dest6, 0);
    274         spans = dest6.getSpans(0, dest6.length(), Object.class);
    275         assertEquals(0, spans.length);
    276 
    277         // exceptional source end
    278         dest6 = new SpannableString("exceptional test");
    279         TextUtils.copySpansFrom(source2, 0, -1, Object.class, dest6, 0);
    280         spans = dest6.getSpans(0, dest6.length(), Object.class);
    281         assertEquals(0, spans.length);
    282         TextUtils.copySpansFrom(source2, 0, Integer.MAX_VALUE, Object.class, dest6, 0);
    283         spans = dest6.getSpans(0, dest6.length(), Object.class);
    284         assertEquals(1, spans.length);
    285 
    286         // exceptional class kind
    287         dest6 = new SpannableString("exceptional test");
    288         TextUtils.copySpansFrom(source2, 0, source2.length(), null, dest6, 0);
    289         spans = dest6.getSpans(0, dest6.length(), Object.class);
    290         assertEquals(1, spans.length);
    291 
    292         // exceptional destination offset
    293         dest6 = new SpannableString("exceptional test");
    294         try {
    295             TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest6, -1);
    296             fail("Should throw IndexOutOfBoundsException");
    297         } catch (IndexOutOfBoundsException e) {
    298             // expect
    299         }
    300         try {
    301             TextUtils.copySpansFrom(source2, 0, source2.length(),
    302                     Object.class, dest6, Integer.MAX_VALUE);
    303             fail("Should throw IndexOutOfBoundsException");
    304         } catch (IndexOutOfBoundsException e) {
    305             // expect
    306         }
    307 
    308         // exceptional source
    309         try {
    310             TextUtils.copySpansFrom(null, 0, source2.length(), Object.class, dest6, 0);
    311             fail("Should throw NullPointerException");
    312         } catch (NullPointerException e) {
    313             // expect
    314         }
    315 
    316         // exceptional destination
    317         try {
    318             TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, null, 0);
    319             fail("Should throw NullPointerException");
    320         } catch (NullPointerException e) {
    321             // expect
    322         }
    323     }
    324 
    325     public void testEllipsize() {
    326         TextPaint p = new TextPaint();
    327 
    328         // turn off kerning. with kerning enabled, different methods of measuring the same text
    329         // produce different results.
    330         p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
    331 
    332         CharSequence text = "long string to truncate";
    333 
    334         float textWidth = p.measureText(mEllipsis) + p.measureText("uncate");
    335         assertEquals(mEllipsis + "uncate",
    336                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString());
    337 
    338         textWidth = p.measureText("long str") + p.measureText(mEllipsis);
    339         assertEquals("long str" + mEllipsis,
    340                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString());
    341 
    342         textWidth = p.measureText("long") + p.measureText(mEllipsis) + p.measureText("ate");
    343         assertEquals("long" + mEllipsis + "ate",
    344                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString());
    345 
    346         // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE.
    347         // In the code it looks like this does the same as MIDDLE.
    348         // In other methods, MARQUEE is equivalent to END, except for the first line.
    349         assertEquals("long" + mEllipsis + "ate",
    350                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE).toString());
    351 
    352         textWidth = p.measureText(mEllipsis);
    353         assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString());
    354         assertEquals("", TextUtils.ellipsize(text, p, textWidth - 1, TruncateAt.END).toString());
    355         assertEquals("", TextUtils.ellipsize(text, p, -1f, TruncateAt.END).toString());
    356         assertEquals(text,
    357                 TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END).toString());
    358 
    359         assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString());
    360         assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString());
    361 
    362         try {
    363             TextUtils.ellipsize(text, null, textWidth, TruncateAt.MIDDLE);
    364             fail("Should throw NullPointerException");
    365         } catch (NullPointerException e) {
    366             // expected
    367         }
    368 
    369         try {
    370             TextUtils.ellipsize(null, p, textWidth, TruncateAt.MIDDLE);
    371             fail("Should throw NullPointerException");
    372         } catch (NullPointerException e) {
    373             // expected
    374         }
    375     }
    376 
    377     public void testEllipsizeCallback() {
    378         TextPaint p = new TextPaint();
    379 
    380         // turn off kerning. with kerning enabled, different methods of measuring the same text
    381         // produce different results.
    382         p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
    383 
    384         TextUtils.EllipsizeCallback callback = new TextUtils.EllipsizeCallback() {
    385             public void ellipsized(final int start, final int end) {
    386                 mStart = start;
    387                 mEnd = end;
    388             }
    389         };
    390 
    391         String text = "long string to truncate";
    392 
    393         // TruncateAt.START, does not specify preserveLength
    394         resetRange();
    395         float textWidth = p.measureText(mEllipsis + "uncate");
    396         assertEquals(mEllipsis + "uncate",
    397                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, false,
    398                         callback).toString());
    399         assertEquals(0, mStart);
    400         assertEquals(text.length() - "uncate".length(), mEnd);
    401 
    402         // TruncateAt.START, specify preserveLength
    403         resetRange();
    404         int ellipsisNum = text.length() - "uncate".length();
    405         assertEquals(getBlankString(true, ellipsisNum) + "uncate",
    406                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, true,
    407                         callback).toString());
    408         assertEquals(0, mStart);
    409         assertEquals(text.length() - "uncate".length(), mEnd);
    410 
    411         // TruncateAt.END, specify preserveLength
    412         resetRange();
    413         textWidth = p.measureText("long str") + p.measureText(mEllipsis);
    414         ellipsisNum = text.length() - "long str".length();
    415         assertEquals("long str" + getBlankString(true, ellipsisNum),
    416                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString());
    417         assertEquals("long str".length(), mStart);
    418         assertEquals(text.length(), mEnd);
    419 
    420         // TruncateAt.MIDDLE, specify preserveLength
    421         resetRange();
    422         textWidth = p.measureText("long" + mEllipsis + "ate");
    423         ellipsisNum = text.length() - "long".length() - "ate".length();
    424         assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
    425                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true,
    426                         callback).toString());
    427         assertEquals("long".length(), mStart);
    428         assertEquals(text.length() - "ate".length(), mEnd);
    429 
    430         // TruncateAt.MIDDLE, specify preserveLength, but does not specify callback.
    431         resetRange();
    432         assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
    433                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true,
    434                         null).toString());
    435         assertEquals(-1, mStart);
    436         assertEquals(-1, mEnd);
    437 
    438         // TruncateAt.MARQUEE, specify preserveLength
    439         // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE.
    440         // In the code it looks like this does the same as MIDDLE.
    441         // In other methods, MARQUEE is equivalent to END, except for the first line.
    442         resetRange();
    443         textWidth = p.measureText("long" + mEllipsis + "ate");
    444         ellipsisNum = text.length() - "long".length() - "ate".length();
    445         assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
    446                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE, true,
    447                         callback).toString());
    448         assertEquals("long".length(), mStart);
    449         assertEquals(text.length() - "ate".length(), mEnd);
    450 
    451         // avail is not long enough for ELLIPSIS, and preserveLength is specified.
    452         resetRange();
    453         textWidth = p.measureText(mEllipsis);
    454         assertEquals(getBlankString(false, text.length()),
    455                 TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, true,
    456                         callback).toString());
    457         assertEquals(0, mStart);
    458         assertEquals(text.length(), mEnd);
    459 
    460         // avail is not long enough for ELLIPSIS, and preserveLength doesn't be specified.
    461         resetRange();
    462         assertEquals("",
    463                 TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, false,
    464                         callback).toString());
    465         assertEquals(0, mStart);
    466         assertEquals(text.length(), mEnd);
    467 
    468         // avail is long enough for ELLIPSIS, and preserveLength is specified.
    469         resetRange();
    470         assertEquals(getBlankString(false, text.length()),
    471                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString());
    472         assertEquals(0, mStart);
    473         assertEquals(text.length(), mEnd);
    474 
    475         // avail is long enough for ELLIPSIS, and preserveLength doesn't be specified.
    476         resetRange();
    477         assertEquals("",
    478                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, false,
    479                         callback).toString());
    480         assertEquals(0, mStart);
    481         assertEquals(text.length(), mEnd);
    482 
    483         // avail is long enough for the whole sentence.
    484         resetRange();
    485         assertEquals(text,
    486                 TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END, true,
    487                         callback).toString());
    488         assertEquals(0, mStart);
    489         assertEquals(0, mEnd);
    490 
    491         textWidth = p.measureText("long str" + mEllipsis);
    492         try {
    493             TextUtils.ellipsize(text, null, textWidth, TruncateAt.END, true, callback);
    494         } catch (NullPointerException e) {
    495             // expected
    496         }
    497 
    498         try {
    499             TextUtils.ellipsize(null, p, textWidth, TruncateAt.END, true, callback);
    500         } catch (NullPointerException e) {
    501             // expected
    502         }
    503     }
    504 
    505     /**
    506      * Get a blank string which is filled up by '\uFEFF'.
    507      *
    508      * @param isNeedStart - boolean whether need to start with char '\u2026' in the string.
    509      * @param len - int length of string.
    510      * @return a blank string which is filled up by '\uFEFF'.
    511      */
    512     private String getBlankString(boolean isNeedStart, int len) {
    513         StringBuilder buf = new StringBuilder();
    514 
    515         int i = 0;
    516         if (isNeedStart) {
    517             buf.append('\u2026');
    518             i++;
    519         }
    520         for (; i < len; i++) {
    521             buf.append('\uFEFF');
    522         }
    523 
    524         return buf.toString();
    525     }
    526 
    527     public void testEquals() {
    528         // compare with itself.
    529         // String is a subclass of CharSequence and overrides equals().
    530         String string = "same object";
    531         assertTrue(TextUtils.equals(string, string));
    532 
    533         // SpannableString is a subclass of CharSequence and does NOT override equals().
    534         SpannableString spanString = new SpannableString("same object");
    535         final String url = "www.test_url.com";
    536         spanString.setSpan(new URLSpan(url), 0, spanString.length(),
    537                 Spanned.SPAN_INCLUSIVE_INCLUSIVE);
    538         assertTrue(TextUtils.equals(spanString, spanString));
    539 
    540         // compare with other objects which have same content.
    541         assertTrue(TextUtils.equals("different object", "different object"));
    542 
    543         SpannableString urlSpanString = new SpannableString("same content");
    544         SpannableString bgColorSpanString = new SpannableString(
    545                 "same content");
    546         URLSpan urlSpan = new URLSpan(url);
    547         urlSpanString.setSpan(urlSpan, 0, urlSpanString.length(),
    548                 Spanned.SPAN_INCLUSIVE_INCLUSIVE);
    549         BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
    550         bgColorSpanString.setSpan(bgColorSpan, 0, bgColorSpanString.length(),
    551                 Spanned.SPAN_INCLUSIVE_INCLUSIVE);
    552 
    553         assertTrue(TextUtils.equals(bgColorSpanString, urlSpanString));
    554 
    555         // compare with other objects which have different content.
    556         assertFalse(TextUtils.equals("different content A", "different content B"));
    557         assertFalse(TextUtils.equals(spanString, urlSpanString));
    558         assertFalse(TextUtils.equals(spanString, bgColorSpanString));
    559 
    560         // compare with null
    561         assertTrue(TextUtils.equals(null, null));
    562         assertFalse(TextUtils.equals(spanString, null));
    563         assertFalse(TextUtils.equals(null, string));
    564     }
    565 
    566     public void testExpandTemplate() {
    567         // ^1 at the start of template string.
    568         assertEquals("value1 template to be expanded",
    569                 TextUtils.expandTemplate("^1 template to be expanded", "value1").toString());
    570         // ^1 at the end of template string.
    571         assertEquals("template to be expanded value1",
    572                 TextUtils.expandTemplate("template to be expanded ^1", "value1").toString());
    573         // ^1 in the middle of template string.
    574         assertEquals("template value1 to be expanded",
    575                 TextUtils.expandTemplate("template ^1 to be expanded", "value1").toString());
    576         // ^1 followed by a '0'
    577         assertEquals("template value10 to be expanded",
    578                 TextUtils.expandTemplate("template ^10 to be expanded", "value1").toString());
    579         // ^1 followed by a 'a'
    580         assertEquals("template value1a to be expanded",
    581                 TextUtils.expandTemplate("template ^1a to be expanded", "value1").toString());
    582         // no ^1
    583         assertEquals("template ^a to be expanded",
    584                 TextUtils.expandTemplate("template ^a to be expanded", "value1").toString());
    585         assertEquals("template to be expanded",
    586                 TextUtils.expandTemplate("template to be expanded", "value1").toString());
    587         // two consecutive ^ in the input to produce a single ^ in the output.
    588         assertEquals("template ^ to be expanded",
    589                 TextUtils.expandTemplate("template ^^ to be expanded", "value1").toString());
    590         // two ^ with a space in the middle.
    591         assertEquals("template ^ ^ to be expanded",
    592                 TextUtils.expandTemplate("template ^ ^ to be expanded", "value1").toString());
    593         // ^1 follow a '^'
    594         assertEquals("template ^1 to be expanded",
    595                 TextUtils.expandTemplate("template ^^1 to be expanded", "value1").toString());
    596         // ^1 followed by a '^'
    597         assertEquals("template value1^ to be expanded",
    598                 TextUtils.expandTemplate("template ^1^ to be expanded", "value1").toString());
    599 
    600         // 9 replacement values
    601         final int MAX_SUPPORTED_VALUES_NUM = 9;
    602         CharSequence values[] = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM);
    603         String expected = "value1 value2 template value3 value4 to value5 value6" +
    604                 " be value7 value8 expanded value9";
    605         String template = "^1 ^2 template ^3 ^4 to ^5 ^6 be ^7 ^8 expanded ^9";
    606         assertEquals(expected, TextUtils.expandTemplate(template, values).toString());
    607 
    608         //  only up to 9 replacement values are supported
    609         values = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM + 1);
    610         try {
    611             TextUtils.expandTemplate(template, values);
    612             fail("Should throw IllegalArgumentException!");
    613         } catch (IllegalArgumentException e) {
    614             // expect
    615         }
    616 
    617         // template string is ^0
    618         try {
    619             TextUtils.expandTemplate("template ^0 to be expanded", "value1");
    620         } catch (IllegalArgumentException e) {
    621             // issue 1695243, doesn't discuss the case that ^0 in template string.
    622         }
    623 
    624         // template string is ^0
    625         try {
    626             TextUtils.expandTemplate("template ^0 to be expanded");
    627         } catch (IllegalArgumentException e) {
    628             // issue 1695243, doesn't discuss the case that ^0 in template string.
    629         }
    630 
    631         // the template requests 2 values but only 1 is provided
    632         try {
    633             TextUtils.expandTemplate("template ^2 to be expanded", "value1");
    634             fail("Should throw IllegalArgumentException!");
    635         } catch (IllegalArgumentException e) {
    636             // expect
    637         }
    638 
    639         // values is null
    640         try {
    641             TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence[]) null);
    642         } catch (NullPointerException e) {
    643             // expected
    644         }
    645 
    646         // the template requests 2 values but only one null value is provided
    647         try {
    648             TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence) null);
    649             fail("Should throw IllegalArgumentException!");
    650         } catch (IllegalArgumentException e) {
    651             // expect
    652         }
    653 
    654         // the template requests 2 values and 2 values is provided, but all values are null.
    655         try {
    656             TextUtils.expandTemplate("template ^2 to be expanded",
    657                     (CharSequence) null, (CharSequence) null);
    658         } catch (NullPointerException e) {
    659             // expected
    660         }
    661 
    662         // the template requests 2 values but no value is provided.
    663         try {
    664             TextUtils.expandTemplate("template ^2 to be expanded");
    665             fail("Should throw IllegalArgumentException!");
    666         } catch (IllegalArgumentException e) {
    667             // expected
    668         }
    669 
    670         // template is null
    671         try {
    672             TextUtils.expandTemplate(null, "value1");
    673         } catch (NullPointerException e) {
    674             // expected
    675         }
    676     }
    677 
    678     /**
    679      * Create a char sequence array with the specified length
    680      * @param len the length of the array
    681      * @return The char sequence array with the specified length.
    682      * The value of each item is "value[index+1]"
    683      */
    684     private CharSequence[] createCharSequenceArray(int len) {
    685         CharSequence array[] = new CharSequence[len];
    686 
    687         for (int i = 0; i < len; i++) {
    688             array[i] = "value" + (i + 1);
    689         }
    690 
    691         return array;
    692     }
    693 
    694     public void testGetChars() {
    695         char[] destOriginal = "destination".toCharArray();
    696         char[] destResult = destOriginal.clone();
    697 
    698         // check whether GetChars.getChars() is called and with the proper parameters.
    699         MockGetChars mockGetChars = new MockGetChars();
    700         int start = 1;
    701         int end = destResult.length;
    702         int destOff = 2;
    703         TextUtils.getChars(mockGetChars, start, end, destResult, destOff);
    704         assertTrue(mockGetChars.hasCalledGetChars());
    705         assertEquals(start, mockGetChars.ReadGetCharsParams().start);
    706         assertEquals(end, mockGetChars.ReadGetCharsParams().end);
    707         assertEquals(destResult, mockGetChars.ReadGetCharsParams().dest);
    708         assertEquals(destOff, mockGetChars.ReadGetCharsParams().destoff);
    709 
    710         // use MockCharSequence to do the test includes corner cases.
    711         MockCharSequence mockCharSequence = new MockCharSequence("source string mock");
    712         // get chars to place at the beginning of the destination except the latest one char.
    713         destResult = destOriginal.clone();
    714         start = 0;
    715         end = destResult.length - 1;
    716         destOff = 0;
    717         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
    718         // chars before end are copied from the mockCharSequence.
    719         for (int i = 0; i < end - start; i++) {
    720             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
    721         }
    722         // chars after end doesn't be changed.
    723         for (int i = destOff + (end - start); i < destOriginal.length; i++) {
    724             assertEquals(destOriginal[i], destResult[i]);
    725         }
    726 
    727         // get chars to place at the end of the destination except the earliest two chars.
    728         destResult = destOriginal.clone();
    729         start = 0;
    730         end = destResult.length - 2;
    731         destOff = 2;
    732         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
    733         // chars before start doesn't be changed.
    734         for (int i = 0; i < destOff; i++) {
    735             assertEquals(destOriginal[i], destResult[i]);
    736         }
    737         // chars after start are copied from the mockCharSequence.
    738         for (int i = 0; i < end - start; i++) {
    739             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
    740         }
    741 
    742         // get chars to place at the end of the destination except the earliest two chars
    743         // and the latest one word.
    744         destResult = destOriginal.clone();
    745         start = 1;
    746         end = destResult.length - 2;
    747         destOff = 0;
    748         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
    749         for (int i = 0; i < destOff; i++) {
    750             assertEquals(destOriginal[i], destResult[i]);
    751         }
    752         for (int i = 0; i < end - start; i++) {
    753             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
    754         }
    755         for (int i = destOff + (end - start); i < destOriginal.length; i++) {
    756             assertEquals(destOriginal[i], destResult[i]);
    757         }
    758 
    759         // get chars to place the whole of the destination
    760         destResult = destOriginal.clone();
    761         start = 0;
    762         end = destResult.length;
    763         destOff = 0;
    764         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
    765         for (int i = 0; i < end - start; i++) {
    766             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
    767         }
    768 
    769         // exceptional start.
    770         end = 2;
    771         destOff = 0;
    772         destResult = destOriginal.clone();
    773         try {
    774             TextUtils.getChars(mockCharSequence, -1, end, destResult, destOff);
    775             fail("Should throw IndexOutOfBoundsException!");
    776         } catch (IndexOutOfBoundsException e) {
    777             // expected
    778         }
    779 
    780         destResult = destOriginal.clone();
    781         TextUtils.getChars(mockCharSequence, Integer.MAX_VALUE, end, destResult, destOff);
    782         for (int i = 0; i < destResult.length; i++) {
    783             assertEquals(destOriginal[i], destResult[i]);
    784         }
    785 
    786         // exceptional end.
    787         destResult = destOriginal.clone();
    788         start = 0;
    789         destOff = 0;
    790         try {
    791             TextUtils.getChars(mockCharSequence, start, destResult.length + 1, destResult, destOff);
    792             fail("Should throw IndexOutOfBoundsException!");
    793         } catch (IndexOutOfBoundsException e) {
    794             // expected
    795         }
    796 
    797         destResult = destOriginal.clone();
    798         TextUtils.getChars(mockCharSequence, start, -1, destResult, destOff);
    799         for (int i = 0; i < destResult.length; i++) {
    800             assertEquals(destOriginal[i], destResult[i]);
    801         }
    802 
    803         // exceptional destOff.
    804         destResult = destOriginal.clone();
    805         start = 0;
    806         end = 2;
    807         try {
    808             TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MAX_VALUE);
    809             fail("Should throw IndexOutOfBoundsException!");
    810         } catch (IndexOutOfBoundsException e) {
    811             // expect
    812         }
    813         try {
    814             TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MIN_VALUE);
    815             fail("Should throw IndexOutOfBoundsException!");
    816         } catch (IndexOutOfBoundsException e) {
    817             // expect
    818         }
    819 
    820         // exceptional source
    821         start = 0;
    822         end = 2;
    823         destOff =0;
    824         try {
    825             TextUtils.getChars(null, start, end, destResult, destOff);
    826             fail("Should throw NullPointerException!");
    827         } catch (NullPointerException e) {
    828             // expected
    829         }
    830 
    831         // exceptional destination
    832         try {
    833             TextUtils.getChars(mockCharSequence, start, end, null, destOff);
    834             fail("Should throw NullPointerException!");
    835         } catch (NullPointerException e) {
    836             // expected
    837         }
    838     }
    839 
    840     /**
    841      * MockGetChars for test.
    842      */
    843     private class MockGetChars implements GetChars {
    844         private boolean mHasCalledGetChars;
    845         private GetCharsParams mGetCharsParams = new GetCharsParams();
    846 
    847         class GetCharsParams {
    848             int start;
    849             int end;
    850             char[] dest;
    851             int destoff;
    852         }
    853 
    854         public boolean hasCalledGetChars() {
    855             return mHasCalledGetChars;
    856         }
    857 
    858         public void reset() {
    859             mHasCalledGetChars = false;
    860         }
    861 
    862         public GetCharsParams ReadGetCharsParams() {
    863             return mGetCharsParams;
    864         }
    865 
    866         public void getChars(int start, int end, char[] dest, int destoff) {
    867             mHasCalledGetChars = true;
    868             mGetCharsParams.start = start;
    869             mGetCharsParams.end = end;
    870             mGetCharsParams.dest = dest;
    871             mGetCharsParams.destoff = destoff;
    872         }
    873 
    874         public char charAt(int arg0) {
    875             return 0;
    876         }
    877 
    878         public int length() {
    879             return 100;
    880         }
    881 
    882         public CharSequence subSequence(int arg0, int arg1) {
    883             return null;
    884         }
    885     }
    886 
    887     /**
    888      * MockCharSequence for test.
    889      */
    890     private class MockCharSequence implements CharSequence {
    891         private char mText[];
    892 
    893         public MockCharSequence() {
    894             this("");
    895         }
    896 
    897         public MockCharSequence(String text) {
    898             mText = text.toCharArray();
    899         }
    900 
    901         public char charAt(int arg0) {
    902             if (arg0 >= 0 && arg0 < mText.length) {
    903                 return mText[arg0];
    904             }
    905             throw new IndexOutOfBoundsException();
    906         }
    907 
    908         public int length() {
    909             return mText.length;
    910         }
    911 
    912         public CharSequence subSequence(int arg0, int arg1) {
    913             return null;
    914         }
    915     }
    916 
    917     public void testGetOffsetAfter() {
    918         // the first '\uD800' is index 9, the second 'uD800' is index 16
    919         // the '\uDBFF' is index 26
    920         final int POS_FIRST_D800 = 9;       // the position of the first '\uD800'.
    921         final int POS_SECOND_D800 = 16;
    922         final int POS_FIRST_DBFF = 26;
    923         final int SUPPLEMENTARY_CHARACTERS_OFFSET = 2;  // the offset for a supplementary characters
    924         final int NORMAL_CHARACTERS_OFFSET = 1;
    925         SpannableString text = new SpannableString(
    926                 "string to\uD800\uDB00 get \uD800\uDC00 offset \uDBFF\uDFFF after");
    927         assertEquals(0 + 1, TextUtils.getOffsetAfter(text, 0));
    928         assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length()));
    929         assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length() - 1));
    930         assertEquals(POS_FIRST_D800 + NORMAL_CHARACTERS_OFFSET,
    931                 TextUtils.getOffsetAfter(text, POS_FIRST_D800));
    932         assertEquals(POS_SECOND_D800 + SUPPLEMENTARY_CHARACTERS_OFFSET,
    933                 TextUtils.getOffsetAfter(text, POS_SECOND_D800));
    934         assertEquals(POS_FIRST_DBFF + SUPPLEMENTARY_CHARACTERS_OFFSET,
    935                 TextUtils.getOffsetAfter(text, POS_FIRST_DBFF));
    936 
    937         // the CharSequence string has a span.
    938         MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
    939         text.setSpan(mockReplacementSpan, POS_FIRST_D800 - 1, text.length() - 1,
    940                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    941         assertEquals(text.length() - 1, TextUtils.getOffsetAfter(text, POS_FIRST_D800));
    942 
    943         try {
    944             TextUtils.getOffsetAfter(text, -1);
    945             fail("Should throw IndexOutOfBoundsException!");
    946         } catch (IndexOutOfBoundsException e) {
    947         }
    948 
    949         try {
    950             TextUtils.getOffsetAfter(text, Integer.MAX_VALUE);
    951             fail("Should throw IndexOutOfBoundsException!");
    952         } catch (IndexOutOfBoundsException e) {
    953         }
    954 
    955         try {
    956             TextUtils.getOffsetAfter(null, 0);
    957             fail("Should throw NullPointerException!");
    958         } catch (NullPointerException e) {
    959             // expected
    960         }
    961     }
    962 
    963     /**
    964      * MockReplacementSpan for test.
    965      */
    966     private class MockReplacementSpan extends ReplacementSpan {
    967         @Override
    968         public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
    969                 int y, int bottom, Paint paint) {
    970         }
    971 
    972         @Override
    973         public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
    974             return 0;
    975         }
    976     }
    977 
    978     public void testGetOffsetBefore() {
    979         // the first '\uDC00' is index 10, the second 'uDC00' is index 17
    980         // the '\uDFFF' is index 27
    981         final int POS_FIRST_DC00 = 10;
    982         final int POS_SECOND_DC00 = 17;
    983         final int POS_FIRST_DFFF = 27;
    984         final int SUPPLYMENTARY_CHARACTERS_OFFSET = 2;
    985         final int NORMAL_CHARACTERS_OFFSET = 1;
    986         SpannableString text = new SpannableString(
    987                 "string to\uD700\uDC00 get \uD800\uDC00 offset \uDBFF\uDFFF before");
    988         assertEquals(0, TextUtils.getOffsetBefore(text, 0));
    989         assertEquals(0, TextUtils.getOffsetBefore(text, 1));
    990         assertEquals(text.length() - 1, TextUtils.getOffsetBefore(text, text.length()));
    991         assertEquals(POS_FIRST_DC00 + 1 - NORMAL_CHARACTERS_OFFSET,
    992                 TextUtils.getOffsetBefore(text, POS_FIRST_DC00 + 1));
    993         assertEquals(POS_SECOND_DC00 + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET,
    994                 TextUtils.getOffsetBefore(text, POS_SECOND_DC00 + 1));
    995         assertEquals(POS_FIRST_DFFF + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET,
    996                 TextUtils.getOffsetBefore(text, POS_FIRST_DFFF + 1));
    997 
    998         // the CharSequence string has a span.
    999         MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
   1000         text.setSpan(mockReplacementSpan, 0, POS_FIRST_DC00 + 1,
   1001                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1002         assertEquals(0, TextUtils.getOffsetBefore(text, POS_FIRST_DC00));
   1003 
   1004         try {
   1005             TextUtils.getOffsetBefore(text, -1);
   1006             fail("Should throw IndexOutOfBoundsException!");
   1007         } catch (IndexOutOfBoundsException e) {
   1008         }
   1009 
   1010         try {
   1011             TextUtils.getOffsetBefore(text, Integer.MAX_VALUE);
   1012             fail("Should throw IndexOutOfBoundsException!");
   1013         } catch (IndexOutOfBoundsException e) {
   1014         }
   1015 
   1016         try {
   1017             TextUtils.getOffsetBefore(null, POS_FIRST_DC00);
   1018             fail("Should throw NullPointerException!");
   1019         } catch (NullPointerException e) {
   1020             // expected
   1021         }
   1022     }
   1023 
   1024     public void testGetReverse() {
   1025         String source = "string to be reversed";
   1026         assertEquals("gnirts", TextUtils.getReverse(source, 0, "string".length()).toString());
   1027         assertEquals("desrever",
   1028                 TextUtils.getReverse(source, source.length() - "reversed".length(),
   1029                         source.length()).toString());
   1030         assertEquals("", TextUtils.getReverse(source, 0, 0).toString());
   1031 
   1032         // issue 1695243, exception is thrown after the result of some cases
   1033         // convert to a string, is this expected?
   1034         CharSequence result = TextUtils.getReverse(source, -1, "string".length());
   1035         try {
   1036             result.toString();
   1037             fail("Should throw IndexOutOfBoundsException!");
   1038         } catch (IndexOutOfBoundsException e) {
   1039         }
   1040 
   1041         TextUtils.getReverse(source, 0, source.length() + 1);
   1042         try {
   1043             result.toString();
   1044             fail("Should throw IndexOutOfBoundsException!");
   1045         } catch (IndexOutOfBoundsException e) {
   1046         }
   1047 
   1048         TextUtils.getReverse(source, "string".length(), 0);
   1049         try {
   1050             result.toString();
   1051             fail("Should throw IndexOutOfBoundsException!");
   1052         } catch (IndexOutOfBoundsException e) {
   1053         }
   1054 
   1055         TextUtils.getReverse(source, 0, Integer.MAX_VALUE);
   1056         try {
   1057             result.toString();
   1058             fail("Should throw IndexOutOfBoundsException!");
   1059         } catch (IndexOutOfBoundsException e) {
   1060         }
   1061 
   1062         TextUtils.getReverse(source, Integer.MIN_VALUE, "string".length());
   1063         try {
   1064             result.toString();
   1065             fail("Should throw IndexOutOfBoundsException!");
   1066         } catch (IndexOutOfBoundsException e) {
   1067         }
   1068 
   1069         TextUtils.getReverse(null, 0, "string".length());
   1070         try {
   1071             result.toString();
   1072             fail("Should throw IndexOutOfBoundsException!");
   1073         } catch (IndexOutOfBoundsException e) {
   1074             // expected
   1075         }
   1076     }
   1077 
   1078     public void testGetTrimmedLength() {
   1079         assertEquals("normalstring".length(), TextUtils.getTrimmedLength("normalstring"));
   1080         assertEquals("normal string".length(), TextUtils.getTrimmedLength("normal string"));
   1081         assertEquals("blank before".length(), TextUtils.getTrimmedLength(" \t  blank before"));
   1082         assertEquals("blank after".length(), TextUtils.getTrimmedLength("blank after   \n    "));
   1083         assertEquals("blank both".length(), TextUtils.getTrimmedLength(" \t   blank both  \n "));
   1084 
   1085         char[] allTrimmedChars = new char[] {
   1086                 '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007',
   1087                 '\u0008', '\u0009', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015',
   1088                 '\u0016', '\u0017', '\u0018', '\u0019', '\u0020'
   1089         };
   1090         assertEquals(0, TextUtils.getTrimmedLength(String.valueOf(allTrimmedChars)));
   1091 
   1092         try {
   1093             TextUtils.getTrimmedLength(null);
   1094             fail("Should throw NullPointerException!");
   1095         } catch (NullPointerException e) {
   1096             // expected
   1097         }
   1098     }
   1099 
   1100     public void testHtmlEncode() {
   1101         assertEquals("&lt;_html_&gt;\\ &amp;&quot;&#39;string&#39;&quot;",
   1102                 TextUtils.htmlEncode("<_html_>\\ &\"'string'\""));
   1103 
   1104          try {
   1105              TextUtils.htmlEncode(null);
   1106              fail("Should throw NullPointerException!");
   1107          } catch (NullPointerException e) {
   1108              // expected
   1109          }
   1110     }
   1111 
   1112     public void testIndexOf1() {
   1113         String searchString = "string to be searched";
   1114         final int INDEX_OF_FIRST_R = 2;     // first occurrence of 'r'
   1115         final int INDEX_OF_FIRST_T = 1;
   1116         final int INDEX_OF_FIRST_D = searchString.length() - 1;
   1117 
   1118         assertEquals(INDEX_OF_FIRST_T, TextUtils.indexOf(searchString, 't'));
   1119         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r'));
   1120         assertEquals(INDEX_OF_FIRST_D, TextUtils.indexOf(searchString, 'd'));
   1121         assertEquals(-1, TextUtils.indexOf(searchString, 'f'));
   1122 
   1123         StringBuffer stringBuffer = new StringBuffer(searchString);
   1124         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuffer, 'r'));
   1125 
   1126         StringBuilder stringBuilder = new StringBuilder(searchString);
   1127         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuilder, 'r'));
   1128 
   1129         MockGetChars mockGetChars = new MockGetChars();
   1130         assertFalse(mockGetChars.hasCalledGetChars());
   1131         TextUtils.indexOf(mockGetChars, 'r');
   1132         assertTrue(mockGetChars.hasCalledGetChars());
   1133 
   1134         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1135         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(mockCharSequence, 'r'));
   1136     }
   1137 
   1138     public void testIndexOf2() {
   1139         String searchString = "string to be searched";
   1140         final int INDEX_OF_FIRST_R = 2;
   1141         final int INDEX_OF_SECOND_R = 16;
   1142 
   1143         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', 0));
   1144         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r', INDEX_OF_FIRST_R + 1));
   1145         assertEquals(-1, TextUtils.indexOf(searchString, 'r', searchString.length()));
   1146         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE));
   1147         assertEquals(-1, TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE));
   1148 
   1149         StringBuffer stringBuffer = new StringBuffer(searchString);
   1150         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r', INDEX_OF_FIRST_R + 1));
   1151         try {
   1152             TextUtils.indexOf(stringBuffer, 'r', Integer.MIN_VALUE);
   1153             fail("Should throw IndexOutOfBoundsException!");
   1154         } catch (IndexOutOfBoundsException e) {
   1155             // expect
   1156         }
   1157         assertEquals(-1, TextUtils.indexOf(stringBuffer, 'r', Integer.MAX_VALUE));
   1158 
   1159         StringBuilder stringBuilder = new StringBuilder(searchString);
   1160         assertEquals(INDEX_OF_SECOND_R,
   1161                 TextUtils.indexOf(stringBuilder, 'r', INDEX_OF_FIRST_R + 1));
   1162 
   1163         MockGetChars mockGetChars = new MockGetChars();
   1164         TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1);
   1165         assertTrue(mockGetChars.hasCalledGetChars());
   1166 
   1167         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1168         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r',
   1169                 INDEX_OF_FIRST_R + 1));
   1170     }
   1171 
   1172     public void testIndexOf3() {
   1173         String searchString = "string to be searched";
   1174         final int INDEX_OF_FIRST_R = 2;
   1175         final int INDEX_OF_SECOND_R = 16;
   1176 
   1177         assertEquals(INDEX_OF_FIRST_R,
   1178                 TextUtils.indexOf(searchString, 'r', 0, searchString.length()));
   1179         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r',
   1180                 INDEX_OF_FIRST_R + 1, searchString.length()));
   1181         assertEquals(-1, TextUtils.indexOf(searchString, 'r',
   1182                 INDEX_OF_FIRST_R + 1, INDEX_OF_SECOND_R));
   1183 
   1184         try {
   1185             TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R);
   1186             fail("Should throw IndexOutOfBoundsException!");
   1187         } catch (IndexOutOfBoundsException e) {
   1188             // expect
   1189         }
   1190         assertEquals(-1,
   1191                 TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE, INDEX_OF_SECOND_R));
   1192         assertEquals(-1, TextUtils.indexOf(searchString, 'r', 0, Integer.MIN_VALUE));
   1193         try {
   1194             TextUtils.indexOf(searchString, 'r', 0, Integer.MAX_VALUE);
   1195             fail("Should throw IndexOutOfBoundsException!");
   1196         } catch (IndexOutOfBoundsException e) {
   1197             // expect
   1198         }
   1199 
   1200         StringBuffer stringBuffer = new StringBuffer(searchString);
   1201         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r',
   1202                 INDEX_OF_FIRST_R + 1, searchString.length()));
   1203 
   1204         StringBuilder stringBuilder = new StringBuilder(searchString);
   1205         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuilder, 'r',
   1206                 INDEX_OF_FIRST_R + 1, searchString.length()));
   1207 
   1208         MockGetChars mockGetChars = new MockGetChars();
   1209         TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1, searchString.length());
   1210         assertTrue(mockGetChars.hasCalledGetChars());
   1211 
   1212         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1213         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r',
   1214                 INDEX_OF_FIRST_R + 1, searchString.length()));
   1215     }
   1216 
   1217     public void testIndexOf4() {
   1218         String searchString = "string to be searched by string";
   1219         final int SEARCH_INDEX = 13;
   1220 
   1221         assertEquals(0, TextUtils.indexOf(searchString, "string"));
   1222         assertEquals(SEARCH_INDEX, TextUtils.indexOf(searchString, "search"));
   1223         assertEquals(-1, TextUtils.indexOf(searchString, "tobe"));
   1224         assertEquals(0, TextUtils.indexOf(searchString, ""));
   1225 
   1226         StringBuffer stringBuffer = new StringBuffer(searchString);
   1227         assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuffer, "search"));
   1228 
   1229         StringBuilder stringBuilder = new StringBuilder(searchString);
   1230         assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuilder, "search"));
   1231 
   1232         MockGetChars mockGetChars = new MockGetChars();
   1233         TextUtils.indexOf(mockGetChars, "search");
   1234         assertTrue(mockGetChars.hasCalledGetChars());
   1235 
   1236         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1237         assertEquals(SEARCH_INDEX, TextUtils.indexOf(mockCharSequence, "search"));
   1238     }
   1239 
   1240     public void testIndexOf5() {
   1241         String searchString = "string to be searched by string";
   1242         final int INDEX_OF_FIRST_STRING = 0;
   1243         final int INDEX_OF_SECOND_STRING = 25;
   1244 
   1245         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0));
   1246         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
   1247                 INDEX_OF_FIRST_STRING + 1));
   1248         assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_SECOND_STRING + 1));
   1249         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string",
   1250                 Integer.MIN_VALUE));
   1251         assertEquals(-1, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE));
   1252 
   1253         assertEquals(1, TextUtils.indexOf(searchString, "", 1));
   1254         assertEquals(Integer.MAX_VALUE, TextUtils.indexOf(searchString, "", Integer.MAX_VALUE));
   1255 
   1256         assertEquals(0, TextUtils.indexOf(searchString, searchString, 0));
   1257         assertEquals(-1, TextUtils.indexOf(searchString, searchString + "longer needle", 0));
   1258 
   1259         StringBuffer stringBuffer = new StringBuffer(searchString);
   1260         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string",
   1261                 INDEX_OF_FIRST_STRING + 1));
   1262         try {
   1263             TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE);
   1264             fail("Should throw IndexOutOfBoundsException!");
   1265         } catch (IndexOutOfBoundsException e) {
   1266             // expect
   1267         }
   1268         assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE));
   1269 
   1270         StringBuilder stringBuilder = new StringBuilder(searchString);
   1271         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string",
   1272                 INDEX_OF_FIRST_STRING + 1));
   1273 
   1274         MockGetChars mockGetChars = new MockGetChars();
   1275         assertFalse(mockGetChars.hasCalledGetChars());
   1276         TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1);
   1277         assertTrue(mockGetChars.hasCalledGetChars());
   1278 
   1279         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1280         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string",
   1281                 INDEX_OF_FIRST_STRING + 1));
   1282     }
   1283 
   1284     public void testIndexOf6() {
   1285         String searchString = "string to be searched by string";
   1286         final int INDEX_OF_FIRST_STRING = 0;
   1287         final int INDEX_OF_SECOND_STRING = 25;
   1288 
   1289         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0,
   1290                 searchString.length()));
   1291         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
   1292                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
   1293         assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_FIRST_STRING + 1,
   1294                 INDEX_OF_SECOND_STRING - 1));
   1295         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string",
   1296                 Integer.MIN_VALUE, INDEX_OF_SECOND_STRING - 1));
   1297         assertEquals(-1, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE,
   1298                 INDEX_OF_SECOND_STRING - 1));
   1299 
   1300         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
   1301                 INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE));
   1302         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
   1303                 INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE));
   1304 
   1305         StringBuffer stringBuffer = new StringBuffer(searchString);
   1306         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string",
   1307                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
   1308         try {
   1309             TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE,
   1310                     INDEX_OF_SECOND_STRING - 1);
   1311             fail("Should throw IndexOutOfBoundsException!");
   1312         } catch (IndexOutOfBoundsException e) {
   1313             // expect
   1314         }
   1315         assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE,
   1316                 searchString.length()));
   1317         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer,
   1318                 "string", INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE));
   1319         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer,
   1320                 "string", INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE));
   1321 
   1322         StringBuilder stringBuilder = new StringBuilder(searchString);
   1323         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string",
   1324                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
   1325 
   1326         MockGetChars mockGetChars = new MockGetChars();
   1327         TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1, searchString.length());
   1328         assertTrue(mockGetChars.hasCalledGetChars());
   1329 
   1330         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1331         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string",
   1332                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
   1333     }
   1334 
   1335     public void testIsDigitsOnly() {
   1336         assertFalse(TextUtils.isDigitsOnly("no digit"));
   1337         assertFalse(TextUtils.isDigitsOnly("character and 56 digits"));
   1338         assertTrue(TextUtils.isDigitsOnly("0123456789"));
   1339         assertFalse(TextUtils.isDigitsOnly("1234 56789"));
   1340 
   1341         try {
   1342             TextUtils.isDigitsOnly(null);
   1343             fail("Should throw NullPointerException!");
   1344         } catch (NullPointerException e) {
   1345             // issue 1695243, not clear what is supposed result if the CharSequence is null.
   1346         }
   1347     }
   1348 
   1349     public void testIsEmpty() {
   1350         assertFalse(TextUtils.isEmpty("not empty"));
   1351         assertFalse(TextUtils.isEmpty("    "));
   1352         assertTrue(TextUtils.isEmpty(""));
   1353         assertTrue(TextUtils.isEmpty(null));
   1354     }
   1355 
   1356     public void testIsGraphicChar() {
   1357         assertTrue(TextUtils.isGraphic('a'));
   1358         assertTrue(TextUtils.isGraphic("\uBA00"));
   1359 
   1360         // LINE_SEPARATOR
   1361         assertFalse(TextUtils.isGraphic('\u2028'));
   1362 
   1363         // PARAGRAPH_SEPARATOR
   1364         assertFalse(TextUtils.isGraphic('\u2029'));
   1365 
   1366         // CONTROL
   1367         assertFalse(TextUtils.isGraphic('\u0085'));
   1368 
   1369         // UNASSIGNED
   1370         assertFalse(TextUtils.isGraphic('\u0D00'));
   1371 
   1372         // SURROGATE
   1373         assertFalse(TextUtils.isGraphic('\uD800'));
   1374 
   1375         // SPACE_SEPARATOR
   1376         assertFalse(TextUtils.isGraphic('\u0020'));
   1377 
   1378         try {
   1379             assertFalse(TextUtils.isGraphic((Character) null));
   1380             fail("Should throw NullPointerException!");
   1381         } catch (NullPointerException e) {
   1382             // expected
   1383         }
   1384     }
   1385 
   1386     public void testIsGraphicCharSequence() {
   1387         assertTrue(TextUtils.isGraphic("printable characters"));
   1388 
   1389         assertFalse(TextUtils.isGraphic("\u2028\u2029\u0085\u0D00\uD800\u0020"));
   1390 
   1391         assertTrue(TextUtils.isGraphic("a\u2028\u2029\u0085\u0D00\uD800\u0020"));
   1392 
   1393         try {
   1394             TextUtils.isGraphic(null);
   1395             fail("Should throw NullPointerException!");
   1396         } catch (NullPointerException e) {
   1397             // expected
   1398         }
   1399     }
   1400 
   1401     @SuppressWarnings("unchecked")
   1402     public void testJoin1() {
   1403         ArrayList<CharSequence> charTokens = new ArrayList<CharSequence>();
   1404         charTokens.add("string1");
   1405         charTokens.add("string2");
   1406         charTokens.add("string3");
   1407         assertEquals("string1|string2|string3", TextUtils.join("|", charTokens));
   1408         assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens));
   1409         assertEquals("string1string2string3", TextUtils.join("", charTokens));
   1410 
   1411         // issue 1695243, not clear what is supposed result if the delimiter or tokens are null.
   1412         assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens));
   1413         try {
   1414             TextUtils.join("|", (Iterable) null);
   1415             fail("Should throw NullPointerException!");
   1416         } catch (NullPointerException e) {
   1417             // expect
   1418         }
   1419 
   1420         ArrayList<SpannableString> spannableStringTokens = new ArrayList<SpannableString>();
   1421         spannableStringTokens.add(new SpannableString("span 1"));
   1422         spannableStringTokens.add(new SpannableString("span 2"));
   1423         spannableStringTokens.add(new SpannableString("span 3"));
   1424         assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
   1425     }
   1426 
   1427     public void testJoin2() {
   1428         CharSequence[] charTokens = new CharSequence[] { "string1", "string2", "string3" };
   1429         assertEquals("string1|string2|string3", TextUtils.join("|", charTokens));
   1430         assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens));
   1431         assertEquals("string1string2string3", TextUtils.join("", charTokens));
   1432 
   1433         // issue 1695243, not clear what is supposed result if the delimiter or tokens are null.
   1434         assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens));
   1435         try {
   1436             TextUtils.join("|", (Object[]) null);
   1437             fail("Should throw NullPointerException!");
   1438         } catch (NullPointerException e) {
   1439             // expect
   1440         }
   1441 
   1442         SpannableString[] spannableStringTokens = new SpannableString[] {
   1443                 new SpannableString("span 1"),
   1444                 new SpannableString("span 2"),
   1445                 new SpannableString("span 3") };
   1446         assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
   1447     }
   1448 
   1449     public void testLastIndexOf1() {
   1450         String searchString = "string to be searched";
   1451         final int INDEX_OF_LAST_R = 16;
   1452         final int INDEX_OF_LAST_T = 7;
   1453         final int INDEX_OF_LAST_D = searchString.length() - 1;
   1454 
   1455         assertEquals(INDEX_OF_LAST_T, TextUtils.lastIndexOf(searchString, 't'));
   1456         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(searchString, 'r'));
   1457         assertEquals(INDEX_OF_LAST_D, TextUtils.lastIndexOf(searchString, 'd'));
   1458         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'f'));
   1459 
   1460         StringBuffer stringBuffer = new StringBuffer(searchString);
   1461         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuffer, 'r'));
   1462 
   1463         StringBuilder stringBuilder = new StringBuilder(searchString);
   1464         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuilder, 'r'));
   1465 
   1466         MockGetChars mockGetChars = new MockGetChars();
   1467         TextUtils.lastIndexOf(mockGetChars, 'r');
   1468         assertTrue(mockGetChars.hasCalledGetChars());
   1469 
   1470         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1471         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(mockCharSequence, 'r'));
   1472     }
   1473 
   1474     public void testLastIndexOf2() {
   1475         String searchString = "string to be searched";
   1476         final int INDEX_OF_FIRST_R = 2;
   1477         final int INDEX_OF_SECOND_R = 16;
   1478 
   1479         assertEquals(INDEX_OF_SECOND_R,
   1480                 TextUtils.lastIndexOf(searchString, 'r', searchString.length()));
   1481         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0));
   1482         assertEquals(INDEX_OF_FIRST_R,
   1483                 TextUtils.lastIndexOf(searchString, 'r', INDEX_OF_FIRST_R));
   1484         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE));
   1485         assertEquals(INDEX_OF_SECOND_R,
   1486                 TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE));
   1487 
   1488         StringBuffer stringBuffer = new StringBuffer(searchString);
   1489         assertEquals(INDEX_OF_FIRST_R,
   1490                 TextUtils.lastIndexOf(stringBuffer, 'r', INDEX_OF_FIRST_R));
   1491         assertEquals(-1, TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MIN_VALUE));
   1492         assertEquals(INDEX_OF_SECOND_R,
   1493                 TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MAX_VALUE));
   1494 
   1495         StringBuilder stringBuilder = new StringBuilder(searchString);
   1496         assertEquals(INDEX_OF_FIRST_R,
   1497                 TextUtils.lastIndexOf(stringBuilder, 'r', INDEX_OF_FIRST_R));
   1498 
   1499         MockGetChars mockGetChars = new MockGetChars();
   1500         TextUtils.lastIndexOf(mockGetChars, 'r', INDEX_OF_FIRST_R);
   1501         assertTrue(mockGetChars.hasCalledGetChars());
   1502 
   1503         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1504         assertEquals(INDEX_OF_FIRST_R,
   1505                 TextUtils.lastIndexOf(mockCharSequence, 'r', INDEX_OF_FIRST_R));
   1506     }
   1507 
   1508     public void testLastIndexOf3() {
   1509         String searchString = "string to be searched";
   1510         final int INDEX_OF_FIRST_R = 2;
   1511         final int INDEX_OF_SECOND_R = 16;
   1512 
   1513         assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0,
   1514                 searchString.length()));
   1515         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(searchString, 'r', 0,
   1516                 INDEX_OF_SECOND_R - 1));
   1517         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, INDEX_OF_FIRST_R - 1));
   1518 
   1519         try {
   1520             TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R - 1);
   1521             fail("Should throw IndexOutOfBoundsException!");
   1522         } catch (IndexOutOfBoundsException e) {
   1523             // expect
   1524         }
   1525         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE,
   1526                 INDEX_OF_SECOND_R - 1));
   1527         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, Integer.MIN_VALUE));
   1528         assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0,
   1529                 Integer.MAX_VALUE));
   1530 
   1531         StringBuffer stringBuffer = new StringBuffer(searchString);
   1532         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuffer, 'r', 0,
   1533                 INDEX_OF_SECOND_R - 1));
   1534 
   1535         StringBuilder stringBuilder = new StringBuilder(searchString);
   1536         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuilder, 'r', 0,
   1537                 INDEX_OF_SECOND_R - 1));
   1538 
   1539         MockGetChars mockGetChars = new MockGetChars();
   1540         TextUtils.lastIndexOf(mockGetChars, 'r', 0, INDEX_OF_SECOND_R - 1);
   1541         assertTrue(mockGetChars.hasCalledGetChars());
   1542 
   1543         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
   1544         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(mockCharSequence, 'r', 0,
   1545                 INDEX_OF_SECOND_R - 1));
   1546     }
   1547 
   1548     public void testRegionMatches() {
   1549         assertFalse(TextUtils.regionMatches("one", 0, "two", 0, "one".length()));
   1550         assertTrue(TextUtils.regionMatches("one", 0, "one", 0, "one".length()));
   1551         try {
   1552             TextUtils.regionMatches("one", 0, "one", 0, "one".length() + 1);
   1553             fail("Should throw IndexOutOfBoundsException!");
   1554         } catch (IndexOutOfBoundsException e) {
   1555         }
   1556 
   1557         String one = "Hello Android, hello World!";
   1558         String two = "Hello World";
   1559         // match "Hello"
   1560         assertTrue(TextUtils.regionMatches(one, 0, two, 0, "Hello".length()));
   1561 
   1562         // match "Hello A" and "Hello W"
   1563         assertFalse(TextUtils.regionMatches(one, 0, two, 0, "Hello A".length()));
   1564 
   1565         // match "World"
   1566         assertTrue(TextUtils.regionMatches(one, "Hello Android, hello ".length(),
   1567                 two, "Hello ".length(), "World".length()));
   1568         assertFalse(TextUtils.regionMatches(one, "Hello Android, hello ".length(),
   1569                 two, 0, "World".length()));
   1570 
   1571         try {
   1572             TextUtils.regionMatches(one, Integer.MIN_VALUE, two, 0, "Hello".length());
   1573             fail("Should throw IndexOutOfBoundsException!");
   1574         } catch (IndexOutOfBoundsException e) {
   1575         }
   1576         try {
   1577             TextUtils.regionMatches(one, Integer.MAX_VALUE, two, 0, "Hello".length());
   1578             fail("Should throw IndexOutOfBoundsException!");
   1579         } catch (IndexOutOfBoundsException e) {
   1580         }
   1581 
   1582         try {
   1583             TextUtils.regionMatches(one, 0, two, Integer.MIN_VALUE, "Hello".length());
   1584             fail("Should throw IndexOutOfBoundsException!");
   1585         } catch (IndexOutOfBoundsException e) {
   1586         }
   1587         try {
   1588             TextUtils.regionMatches(one, 0, two, Integer.MAX_VALUE, "Hello".length());
   1589             fail("Should throw IndexOutOfBoundsException!");
   1590         } catch (IndexOutOfBoundsException e) {
   1591         }
   1592 
   1593         try {
   1594             TextUtils.regionMatches(one, 0, two, 0, Integer.MIN_VALUE);
   1595             fail("Should throw IndexOutOfBoundsException!");
   1596         } catch (IndexOutOfBoundsException e) {
   1597         }
   1598         try {
   1599             TextUtils.regionMatches(one, 0, two, 0, Integer.MAX_VALUE);
   1600             fail("Should throw IndexOutOfBoundsException!");
   1601         } catch (IndexOutOfBoundsException e) {
   1602         }
   1603 
   1604         try {
   1605             TextUtils.regionMatches(null, 0, two, 0, "Hello".length());
   1606             fail("Should throw NullPointerException!");
   1607         } catch (NullPointerException e) {
   1608             // expect
   1609         }
   1610         try {
   1611             TextUtils.regionMatches(one, 0, null, 0, "Hello".length());
   1612             fail("Should throw NullPointerException!");
   1613         } catch (NullPointerException e) {
   1614             // expect
   1615         }
   1616     }
   1617 
   1618     public void testReplace() {
   1619         String template = "this is a string to be as the template for replacement";
   1620 
   1621         String sources[] = new String[] { "string" };
   1622         CharSequence destinations[] = new CharSequence[] { "text" };
   1623         SpannableStringBuilder replacedString = (SpannableStringBuilder) TextUtils.replace(template,
   1624                 sources, destinations);
   1625         assertEquals("this is a text to be as the template for replacement",
   1626                 replacedString.toString());
   1627 
   1628         sources = new String[] {"is", "the", "for replacement"};
   1629         destinations = new CharSequence[] {"was", "", "to be replaced"};
   1630         replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations);
   1631         assertEquals("thwas is a string to be as  template to be replaced",
   1632                 replacedString.toString());
   1633 
   1634         sources = new String[] {"is", "for replacement"};
   1635         destinations = new CharSequence[] {"was", "", "to be replaced"};
   1636         replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations);
   1637         assertEquals("thwas is a string to be as the template ", replacedString.toString());
   1638 
   1639         sources = new String[] {"is", "the", "for replacement"};
   1640         destinations = new CharSequence[] {"was", "to be replaced"};
   1641         try {
   1642             TextUtils.replace(template, sources, destinations);
   1643             fail("Should throw ArrayIndexOutOfBoundsException!");
   1644         } catch (ArrayIndexOutOfBoundsException e) {
   1645             // expected
   1646         }
   1647 
   1648         try {
   1649             TextUtils.replace(null, sources, destinations);
   1650             fail("Should throw NullPointerException!");
   1651         } catch (NullPointerException e) {
   1652             // expected
   1653         }
   1654         try {
   1655             TextUtils.replace(template, null, destinations);
   1656             fail("Should throw NullPointerException!");
   1657         } catch (NullPointerException e) {
   1658             // expected
   1659         }
   1660         try {
   1661             TextUtils.replace(template, sources, null);
   1662             fail("Should throw NullPointerException!");
   1663         } catch (NullPointerException e) {
   1664             // expected
   1665         }
   1666     }
   1667 
   1668     public void testSplitPattern() {
   1669         String testString = "abccbadecdebz";
   1670         assertEquals(calculateCharsCount(testString, "c") + 1,
   1671                 TextUtils.split(testString, Pattern.compile("c")).length);
   1672         assertEquals(calculateCharsCount(testString, "a") + 1,
   1673                 TextUtils.split(testString, Pattern.compile("a")).length);
   1674         assertEquals(calculateCharsCount(testString, "z") + 1,
   1675                 TextUtils.split(testString, Pattern.compile("z")).length);
   1676         assertEquals(calculateCharsCount(testString, "de") + 1,
   1677                 TextUtils.split(testString, Pattern.compile("de")).length);
   1678         int totalCount = 1 + calculateCharsCount(testString, "a")
   1679                 + calculateCharsCount(testString, "b") + calculateCharsCount(testString, "c");
   1680         assertEquals(totalCount,
   1681                 TextUtils.split(testString, Pattern.compile("[a-c]")).length);
   1682         assertEquals(0, TextUtils.split("", Pattern.compile("a")).length);
   1683         // issue 1695243, not clear what is supposed result if the pattern string is empty.
   1684         assertEquals(testString.length() + 2,
   1685                 TextUtils.split(testString, Pattern.compile("")).length);
   1686 
   1687         try {
   1688             TextUtils.split(null, Pattern.compile("a"));
   1689             fail("Should throw NullPointerException!");
   1690         } catch (NullPointerException e) {
   1691             // expect
   1692         }
   1693         try {
   1694             TextUtils.split("abccbadecdebz", (Pattern) null);
   1695             fail("Should throw NullPointerException!");
   1696         } catch (NullPointerException e) {
   1697             // expect
   1698         }
   1699     }
   1700 
   1701     /*
   1702      * return the appearance count of searched chars in text.
   1703      */
   1704     private int calculateCharsCount(CharSequence text, CharSequence searches) {
   1705         int count = 0;
   1706         int start = TextUtils.indexOf(text, searches, 0);
   1707 
   1708         while (start != -1) {
   1709             count++;
   1710             start = TextUtils.indexOf(text, searches, start + 1);
   1711         }
   1712         return count;
   1713     }
   1714 
   1715     public void testSplitString() {
   1716         String testString = "abccbadecdebz";
   1717         assertEquals(calculateCharsCount(testString, "c") + 1,
   1718                 TextUtils.split("abccbadecdebz", "c").length);
   1719         assertEquals(calculateCharsCount(testString, "a") + 1,
   1720                 TextUtils.split("abccbadecdebz", "a").length);
   1721         assertEquals(calculateCharsCount(testString, "z") + 1,
   1722                 TextUtils.split("abccbadecdebz", "z").length);
   1723         assertEquals(calculateCharsCount(testString, "de") + 1,
   1724                 TextUtils.split("abccbadecdebz", "de").length);
   1725         assertEquals(0, TextUtils.split("", "a").length);
   1726         // issue 1695243, not clear what is supposed result if the pattern string is empty.
   1727         assertEquals(testString.length() + 2,
   1728                 TextUtils.split("abccbadecdebz", "").length);
   1729 
   1730         try {
   1731             TextUtils.split(null, "a");
   1732             fail("Should throw NullPointerException!");
   1733         } catch (NullPointerException e) {
   1734             // expect
   1735         }
   1736         try {
   1737             TextUtils.split("abccbadecdebz", (String) null);
   1738             fail("Should throw NullPointerException!");
   1739         } catch (NullPointerException e) {
   1740             // expect
   1741         }
   1742     }
   1743 
   1744     public void testStringOrSpannedString() {
   1745         assertNull(TextUtils.stringOrSpannedString(null));
   1746 
   1747         SpannedString spannedString = new SpannedString("Spanned String");
   1748         assertSame(spannedString, TextUtils.stringOrSpannedString(spannedString));
   1749 
   1750         SpannableString spannableString = new SpannableString("Spannable String");
   1751         assertEquals("Spannable String",
   1752                 TextUtils.stringOrSpannedString(spannableString).toString());
   1753         assertEquals(SpannedString.class,
   1754                 TextUtils.stringOrSpannedString(spannableString).getClass());
   1755 
   1756         StringBuffer stringBuffer = new StringBuffer("String Buffer");
   1757         assertEquals("String Buffer",
   1758                 TextUtils.stringOrSpannedString(stringBuffer).toString());
   1759         assertEquals(String.class,
   1760                 TextUtils.stringOrSpannedString(stringBuffer).getClass());
   1761     }
   1762 
   1763     public void testSubString() {
   1764         String string = "String";
   1765         assertSame(string, TextUtils.substring(string, 0, string.length()));
   1766         assertEquals("Strin", TextUtils.substring(string, 0, string.length() - 1));
   1767         assertEquals("", TextUtils.substring(string, 1, 1));
   1768 
   1769         try {
   1770             TextUtils.substring(string, string.length(), 0);
   1771             fail("Should throw IndexOutOfBoundsException!");
   1772         } catch (IndexOutOfBoundsException e) {
   1773             // expected
   1774         }
   1775 
   1776         try {
   1777             TextUtils.substring(string, -1, string.length());
   1778             fail("Should throw IndexOutOfBoundsException!");
   1779         } catch (IndexOutOfBoundsException e) {
   1780             // expected
   1781         }
   1782 
   1783         try {
   1784             TextUtils.substring(string, Integer.MAX_VALUE, string.length());
   1785             fail("Should throw IndexOutOfBoundsException!");
   1786         } catch (IndexOutOfBoundsException e) {
   1787             // expected
   1788         }
   1789 
   1790         try {
   1791             TextUtils.substring(string, 0, -1);
   1792             fail("Should throw IndexOutOfBoundsException!");
   1793         } catch (IndexOutOfBoundsException e) {
   1794             // expected
   1795         }
   1796 
   1797         try {
   1798             TextUtils.substring(string, 0, Integer.MAX_VALUE);
   1799             fail("Should throw IndexOutOfBoundsException!");
   1800         } catch (IndexOutOfBoundsException e) {
   1801             // expected
   1802         }
   1803 
   1804         try {
   1805             TextUtils.substring(null, 0, string.length());
   1806             fail("Should throw NullPointerException!");
   1807         } catch (NullPointerException e) {
   1808             // expected
   1809         }
   1810 
   1811         StringBuffer stringBuffer = new StringBuffer("String Buffer");
   1812         assertEquals("Strin", TextUtils.substring(stringBuffer, 0, string.length() - 1));
   1813         assertEquals("", TextUtils.substring(stringBuffer, 1, 1));
   1814 
   1815         MockGetChars mockGetChars = new MockGetChars();
   1816         TextUtils.substring(mockGetChars, 0, string.length());
   1817         assertTrue(mockGetChars.hasCalledGetChars());
   1818     }
   1819 
   1820     public void testWriteToParcel() {
   1821         Parcelable.Creator<CharSequence> creator = TextUtils.CHAR_SEQUENCE_CREATOR;
   1822         String string = "String";
   1823         Parcel p = Parcel.obtain();
   1824         try {
   1825             TextUtils.writeToParcel(string, p, 0);
   1826             p.setDataPosition(0);
   1827             assertEquals(string, creator.createFromParcel(p).toString());
   1828         } finally {
   1829             p.recycle();
   1830         }
   1831 
   1832         p = Parcel.obtain();
   1833         try {
   1834             TextUtils.writeToParcel(null, p, 0);
   1835             p.setDataPosition(0);
   1836             assertNull(creator.createFromParcel(p));
   1837         } finally {
   1838             p.recycle();
   1839         }
   1840 
   1841         SpannableString spannableString = new SpannableString("Spannable String");
   1842         int urlSpanStart = spannableString.length() >> 1;
   1843         int urlSpanEnd = spannableString.length();
   1844         p = Parcel.obtain();
   1845         try {
   1846             URLSpan urlSpan = new URLSpan("URL Span");
   1847             spannableString.setSpan(urlSpan, urlSpanStart, urlSpanEnd,
   1848                     Spanned.SPAN_INCLUSIVE_INCLUSIVE);
   1849             TextUtils.writeToParcel(spannableString, p, 0);
   1850             p.setDataPosition(0);
   1851             SpannableString ret = (SpannableString) creator.createFromParcel(p);
   1852             assertEquals("Spannable String", ret.toString());
   1853             Object[] spans = ret.getSpans(0, ret.length(), Object.class);
   1854             assertEquals(1, spans.length);
   1855             assertEquals("URL Span", ((URLSpan) spans[0]).getURL());
   1856             assertEquals(urlSpanStart, ret.getSpanStart(spans[0]));
   1857             assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0]));
   1858             assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0]));
   1859         } finally {
   1860             p.recycle();
   1861         }
   1862 
   1863         p = Parcel.obtain();
   1864         try {
   1865             ColorStateList colors = new ColorStateList(new int[][] {
   1866                     new int[] {android.R.attr.state_focused}, new int[0]},
   1867                     new int[] {Color.rgb(0, 255, 0), Color.BLACK});
   1868             int textSize = 20;
   1869             TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(
   1870                     null, Typeface.ITALIC, textSize, colors, null);
   1871             int textAppearanceSpanStart = 0;
   1872             int textAppearanceSpanEnd = spannableString.length() >> 1;
   1873             spannableString.setSpan(textAppearanceSpan, textAppearanceSpanStart,
   1874                     textAppearanceSpanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
   1875             TextUtils.writeToParcel(spannableString, p, -1);
   1876             p.setDataPosition(0);
   1877             SpannableString ret = (SpannableString) creator.createFromParcel(p);
   1878             assertEquals("Spannable String", ret.toString());
   1879             Object[] spans = ret.getSpans(0, ret.length(), Object.class);
   1880             assertEquals(2, spans.length);
   1881             assertEquals("URL Span", ((URLSpan) spans[0]).getURL());
   1882             assertEquals(urlSpanStart, ret.getSpanStart(spans[0]));
   1883             assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0]));
   1884             assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0]));
   1885             assertEquals(null, ((TextAppearanceSpan) spans[1]).getFamily());
   1886 
   1887             assertEquals(Typeface.ITALIC, ((TextAppearanceSpan) spans[1]).getTextStyle());
   1888             assertEquals(textSize, ((TextAppearanceSpan) spans[1]).getTextSize());
   1889 
   1890             assertEquals(colors.toString(), ((TextAppearanceSpan) spans[1]).getTextColor().toString());
   1891             assertEquals(null, ((TextAppearanceSpan) spans[1]).getLinkTextColor());
   1892             assertEquals(textAppearanceSpanStart, ret.getSpanStart(spans[1]));
   1893             assertEquals(textAppearanceSpanEnd, ret.getSpanEnd(spans[1]));
   1894             assertEquals(Spanned.SPAN_INCLUSIVE_EXCLUSIVE, ret.getSpanFlags(spans[1]));
   1895         } finally {
   1896             p.recycle();
   1897         }
   1898 
   1899         try {
   1900             TextUtils.writeToParcel(spannableString, null, 0);
   1901             fail("Should throw NullPointerException!");
   1902         } catch (NullPointerException e) {
   1903             // expected
   1904         }
   1905     }
   1906 
   1907     public void testGetCapsMode() {
   1908         final int CAP_MODE_ALL = TextUtils.CAP_MODE_CHARACTERS
   1909                 | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES;
   1910         final int CAP_MODE_CHARACTERS_AND_WORD =
   1911                 TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS;
   1912         String testString = "Start. Sentence word!No space before\n\t" +
   1913                 "Paragraph? (\"\'skip begin\'\"). skip end";
   1914 
   1915         // CAP_MODE_SENTENCES should be in effect in the whole text.
   1916         for (int i = 0; i < testString.length(); i++) {
   1917             assertEquals(TextUtils.CAP_MODE_CHARACTERS,
   1918                     TextUtils.getCapsMode(testString, i, TextUtils.CAP_MODE_CHARACTERS));
   1919         }
   1920 
   1921         // all modes should be in effect at the start of the text.
   1922         assertEquals(TextUtils.CAP_MODE_WORDS,
   1923                 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_WORDS));
   1924         // issue 1586346
   1925         assertEquals(TextUtils.CAP_MODE_WORDS,
   1926                 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_SENTENCES));
   1927         assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
   1928                 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL));
   1929 
   1930         // all mode should be in effect at the position after "." or "?" or "!" + " ".
   1931         int offset = testString.indexOf("Sentence word!");
   1932         assertEquals(TextUtils.CAP_MODE_WORDS,
   1933                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
   1934         assertEquals(TextUtils.CAP_MODE_SENTENCES,
   1935                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
   1936         // issue 1586346
   1937         assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
   1938                 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL));
   1939 
   1940         // CAP_MODE_SENTENCES should NOT be in effect at the position after other words + " ".
   1941         offset = testString.indexOf("word!");
   1942         assertEquals(TextUtils.CAP_MODE_WORDS,
   1943                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
   1944         assertEquals(0,
   1945                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
   1946         // issue 1586346
   1947         assertEquals(TextUtils.CAP_MODE_CHARACTERS,
   1948                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
   1949 
   1950         // if no space after "." or "?" or "!", CAP_MODE_SENTENCES and CAP_MODE_WORDS
   1951         // should NOT be in effect.
   1952         offset = testString.indexOf("No space before");
   1953         assertEquals(0,
   1954                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
   1955         assertEquals(0,
   1956                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
   1957         assertEquals(TextUtils.CAP_MODE_CHARACTERS,
   1958                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
   1959 
   1960         // all mode should be in effect at a beginning of a new paragraph.
   1961         offset = testString.indexOf("Paragraph");
   1962         assertEquals(TextUtils.CAP_MODE_WORDS,
   1963                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
   1964         // issue 1586346
   1965         assertEquals(TextUtils.CAP_MODE_WORDS,
   1966                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
   1967         assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
   1968                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
   1969 
   1970         // some special word which means the start of a sentence should be skipped.
   1971         offset = testString.indexOf("skip begin");
   1972         assertEquals(TextUtils.CAP_MODE_WORDS,
   1973                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
   1974         assertEquals(TextUtils.CAP_MODE_SENTENCES,
   1975                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
   1976         // issue 1586346
   1977         assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS,
   1978                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
   1979 
   1980         // some special word which means the end of a sentence should be skipped.
   1981         offset = testString.indexOf("skip end");
   1982         assertEquals(TextUtils.CAP_MODE_WORDS,
   1983                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
   1984         assertEquals(TextUtils.CAP_MODE_SENTENCES,
   1985                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
   1986         // issue 1586346
   1987         assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS,
   1988                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
   1989     }
   1990 
   1991     public void testGetCapsModeException() {
   1992         String testString = "Start. Sentence word!No space before\n\t" +
   1993                 "Paragraph? (\"\'skip begin\'\"). skip end";
   1994 
   1995         int offset = testString.indexOf("Sentence word!");
   1996         assertEquals(TextUtils.CAP_MODE_CHARACTERS,
   1997                 TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_CHARACTERS));
   1998 
   1999         try {
   2000             TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_SENTENCES);
   2001             fail("Should throw NullPointerException!");
   2002         } catch (NullPointerException e) {
   2003             // expected
   2004         }
   2005 
   2006         assertEquals(0, TextUtils.getCapsMode(testString, -1, TextUtils.CAP_MODE_SENTENCES));
   2007 
   2008         try {
   2009             TextUtils.getCapsMode(testString, testString.length() + 1,
   2010                     TextUtils.CAP_MODE_SENTENCES);
   2011             fail("Should throw IndexOutOfBoundsException!");
   2012         } catch (IndexOutOfBoundsException e) {
   2013             // expected
   2014         }
   2015     }
   2016 
   2017     public void testDumpSpans() {
   2018         StringBuilder builder = new StringBuilder();
   2019         StringBuilderPrinter printer = new StringBuilderPrinter(builder);
   2020         CharSequence source = "test dump spans";
   2021         String prefix = "prefix";
   2022 
   2023         assertEquals(0, builder.length());
   2024         TextUtils.dumpSpans(source, printer, prefix);
   2025         assertTrue(builder.length() > 0);
   2026 
   2027         builder = new StringBuilder();
   2028         printer = new StringBuilderPrinter(builder);
   2029         assertEquals(0, builder.length());
   2030         SpannableString spanned = new SpannableString(source);
   2031         spanned.setSpan(new Object(), 0, source.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
   2032         TextUtils.dumpSpans(spanned, printer, prefix);
   2033         assertTrue(builder.length() > 0);
   2034     }
   2035 
   2036     @TestTargetNew(
   2037             level = TestLevel.COMPLETE,
   2038             method = "getLayoutDirectionFromLocale",
   2039             args = {Locale.class}
   2040     )
   2041     public void testGetLayoutDirectionFromLocale() {
   2042         assertEquals(LAYOUT_DIRECTION_LTR,
   2043                 TextUtils.getLayoutDirectionFromLocale(null));
   2044 
   2045         assertEquals(LAYOUT_DIRECTION_LTR,
   2046                 TextUtils.getLayoutDirectionFromLocale(Locale.ENGLISH));
   2047         assertEquals(LAYOUT_DIRECTION_LTR,
   2048                 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA));
   2049         assertEquals(LAYOUT_DIRECTION_LTR,
   2050                 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
   2051         assertEquals(LAYOUT_DIRECTION_LTR,
   2052                 TextUtils.getLayoutDirectionFromLocale(Locale.FRANCE));
   2053         assertEquals(LAYOUT_DIRECTION_LTR,
   2054                 TextUtils.getLayoutDirectionFromLocale(Locale.FRENCH));
   2055         assertEquals(LAYOUT_DIRECTION_LTR,
   2056                 TextUtils.getLayoutDirectionFromLocale(Locale.GERMAN));
   2057         assertEquals(LAYOUT_DIRECTION_LTR,
   2058                 TextUtils.getLayoutDirectionFromLocale(Locale.GERMANY));
   2059         assertEquals(LAYOUT_DIRECTION_LTR,
   2060                 TextUtils.getLayoutDirectionFromLocale(Locale.ITALIAN));
   2061         assertEquals(LAYOUT_DIRECTION_LTR,
   2062                 TextUtils.getLayoutDirectionFromLocale(Locale.ITALY));
   2063         assertEquals(LAYOUT_DIRECTION_LTR,
   2064                 TextUtils.getLayoutDirectionFromLocale(Locale.UK));
   2065         assertEquals(LAYOUT_DIRECTION_LTR,
   2066                 TextUtils.getLayoutDirectionFromLocale(Locale.US));
   2067 
   2068         assertEquals(LAYOUT_DIRECTION_LTR,
   2069                 TextUtils.getLayoutDirectionFromLocale(Locale.ROOT));
   2070 
   2071         assertEquals(LAYOUT_DIRECTION_LTR,
   2072                 TextUtils.getLayoutDirectionFromLocale(Locale.CHINA));
   2073         assertEquals(LAYOUT_DIRECTION_LTR,
   2074                 TextUtils.getLayoutDirectionFromLocale(Locale.CHINESE));
   2075         assertEquals(LAYOUT_DIRECTION_LTR,
   2076                 TextUtils.getLayoutDirectionFromLocale(Locale.JAPAN));
   2077         assertEquals(LAYOUT_DIRECTION_LTR,
   2078                 TextUtils.getLayoutDirectionFromLocale(Locale.JAPANESE));
   2079         assertEquals(LAYOUT_DIRECTION_LTR,
   2080                 TextUtils.getLayoutDirectionFromLocale(Locale.KOREA));
   2081         assertEquals(LAYOUT_DIRECTION_LTR,
   2082                 TextUtils.getLayoutDirectionFromLocale(Locale.KOREAN));
   2083         assertEquals(LAYOUT_DIRECTION_LTR,
   2084                 TextUtils.getLayoutDirectionFromLocale(Locale.PRC));
   2085         assertEquals(LAYOUT_DIRECTION_LTR,
   2086                 TextUtils.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
   2087         assertEquals(LAYOUT_DIRECTION_LTR,
   2088                 TextUtils.getLayoutDirectionFromLocale(Locale.TAIWAN));
   2089         assertEquals(LAYOUT_DIRECTION_LTR,
   2090                 TextUtils.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
   2091 
   2092         // Some languages always use an RTL script.
   2093         for (Locale l : Locale.getAvailableLocales()) {
   2094             String languageCode = l.getLanguage();
   2095             if (languageCode.equals("ar") ||
   2096                     languageCode.equals("fa") ||
   2097                     languageCode.equals("iw") ||
   2098                     languageCode.equals("he") ||
   2099                     languageCode.equals("ps") ||
   2100                     languageCode.equals("ur")) {
   2101                 int direction = TextUtils.getLayoutDirectionFromLocale(l);
   2102                 assertEquals(l.toLanguageTag() + " not RTL: " + direction,
   2103                              LAYOUT_DIRECTION_RTL, direction);
   2104             }
   2105         }
   2106 
   2107         // Other languages have some cases where they use an RTL script.
   2108         String[] tags = {
   2109             "pa-Arab",
   2110             "pa-Arab-PK",
   2111             "ps",
   2112             "ps-AF",
   2113             "uz-Arab",
   2114             "uz-Arab-AF",
   2115         };
   2116         for (String tag : tags) {
   2117             Locale l = Locale.forLanguageTag(tag);
   2118             int direction = TextUtils.getLayoutDirectionFromLocale(l);
   2119             assertEquals(l.toLanguageTag() + " not RTL: " + direction,
   2120                          LAYOUT_DIRECTION_RTL, direction);
   2121         }
   2122 
   2123         // Locale without a real language
   2124         Locale locale = Locale.forLanguageTag("zz");
   2125         assertEquals(LAYOUT_DIRECTION_LTR,
   2126                 TextUtils.getLayoutDirectionFromLocale(locale));
   2127     }
   2128 }
   2129