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