1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.lang; 19 20 import libcore.util.EmptyArray; 21 22 import java.io.InvalidObjectException; 23 import java.util.Arrays; 24 25 /** 26 * A modifiable {@link CharSequence sequence of characters} for use in creating 27 * and modifying Strings. This class is intended as a base class for 28 * {@link StringBuffer} and {@link StringBuilder}. 29 * 30 * @see StringBuffer 31 * @see StringBuilder 32 * @since 1.5 33 */ 34 abstract class AbstractStringBuilder { 35 36 static final int INITIAL_CAPACITY = 16; 37 38 private char[] value; 39 40 private int count; 41 42 private boolean shared; 43 44 /* 45 * Returns the character array. 46 */ getValue()47 final char[] getValue() { 48 return value; 49 } 50 51 /* 52 * Returns the underlying buffer and sets the shared flag. 53 */ shareValue()54 final char[] shareValue() { 55 shared = true; 56 return value; 57 } 58 59 /* 60 * Restores internal state after deserialization. 61 */ set(char[] val, int len)62 final void set(char[] val, int len) throws InvalidObjectException { 63 if (val == null) { 64 val = EmptyArray.CHAR; 65 } 66 if (val.length < len) { 67 throw new InvalidObjectException("count out of range"); 68 } 69 70 shared = false; 71 value = val; 72 count = len; 73 } 74 AbstractStringBuilder()75 AbstractStringBuilder() { 76 value = new char[INITIAL_CAPACITY]; 77 } 78 AbstractStringBuilder(int capacity)79 AbstractStringBuilder(int capacity) { 80 if (capacity < 0) { 81 throw new NegativeArraySizeException(Integer.toString(capacity)); 82 } 83 value = new char[capacity]; 84 } 85 AbstractStringBuilder(String string)86 AbstractStringBuilder(String string) { 87 count = string.length(); 88 shared = false; 89 value = new char[count + INITIAL_CAPACITY]; 90 string._getChars(0, count, value, 0); 91 } 92 enlargeBuffer(int min)93 private void enlargeBuffer(int min) { 94 int newCount = ((value.length >> 1) + value.length) + 2; 95 char[] newData = new char[min > newCount ? min : newCount]; 96 System.arraycopy(value, 0, newData, 0, count); 97 value = newData; 98 shared = false; 99 } 100 appendNull()101 final void appendNull() { 102 int newCount = count + 4; 103 if (newCount > value.length) { 104 enlargeBuffer(newCount); 105 } 106 value[count++] = 'n'; 107 value[count++] = 'u'; 108 value[count++] = 'l'; 109 value[count++] = 'l'; 110 } 111 append0(char[] chars)112 final void append0(char[] chars) { 113 int newCount = count + chars.length; 114 if (newCount > value.length) { 115 enlargeBuffer(newCount); 116 } 117 System.arraycopy(chars, 0, value, count, chars.length); 118 count = newCount; 119 } 120 append0(char[] chars, int offset, int length)121 final void append0(char[] chars, int offset, int length) { 122 Arrays.checkOffsetAndCount(chars.length, offset, length); 123 int newCount = count + length; 124 if (newCount > value.length) { 125 enlargeBuffer(newCount); 126 } 127 System.arraycopy(chars, offset, value, count, length); 128 count = newCount; 129 } 130 append0(char ch)131 final void append0(char ch) { 132 if (count == value.length) { 133 enlargeBuffer(count + 1); 134 } 135 value[count++] = ch; 136 } 137 append0(String string)138 final void append0(String string) { 139 if (string == null) { 140 appendNull(); 141 return; 142 } 143 int length = string.length(); 144 int newCount = count + length; 145 if (newCount > value.length) { 146 enlargeBuffer(newCount); 147 } 148 string._getChars(0, length, value, count); 149 count = newCount; 150 } 151 append0(CharSequence s, int start, int end)152 final void append0(CharSequence s, int start, int end) { 153 if (s == null) { 154 s = "null"; 155 } 156 if ((start | end) < 0 || start > end || end > s.length()) { 157 throw new IndexOutOfBoundsException(); 158 } 159 160 int length = end - start; 161 int newCount = count + length; 162 if (newCount > value.length) { 163 enlargeBuffer(newCount); 164 } else if (shared) { 165 value = value.clone(); 166 shared = false; 167 } 168 169 if (s instanceof String) { 170 ((String) s)._getChars(start, end, value, count); 171 } else if (s instanceof AbstractStringBuilder) { 172 AbstractStringBuilder other = (AbstractStringBuilder) s; 173 System.arraycopy(other.value, start, value, count, length); 174 } else { 175 int j = count; // Destination index. 176 for (int i = start; i < end; i++) { 177 value[j++] = s.charAt(i); 178 } 179 } 180 181 this.count = newCount; 182 } 183 184 /** 185 * Returns the number of characters that can be held without growing. 186 * 187 * @return the capacity 188 * @see #ensureCapacity 189 * @see #length 190 */ capacity()191 public int capacity() { 192 return value.length; 193 } 194 195 /** 196 * Returns the character at {@code index}. 197 * @throws IndexOutOfBoundsException if {@code index < 0} or {@code index >= length()}. 198 */ charAt(int index)199 public char charAt(int index) { 200 if (index < 0 || index >= count) { 201 throw indexAndLength(index); 202 } 203 return value[index]; 204 } 205 indexAndLength(int index)206 private StringIndexOutOfBoundsException indexAndLength(int index) { 207 throw new StringIndexOutOfBoundsException(count, index); 208 } 209 startEndAndLength(int start, int end)210 private StringIndexOutOfBoundsException startEndAndLength(int start, int end) { 211 throw new StringIndexOutOfBoundsException(count, start, end - start); 212 } 213 delete0(int start, int end)214 final void delete0(int start, int end) { 215 // NOTE: StringBuilder#delete(int, int) is specified not to throw if 216 // the end index is >= count, as long as it's >= start. This means 217 // we have to clamp it to count here. 218 if (end > count) { 219 end = count; 220 } 221 222 if (start < 0 || start > count || start > end) { 223 throw startEndAndLength(start, end); 224 } 225 226 // NOTE: StringBuilder#delete(int, int) throws only if start > count 227 // (start == count is considered valid, oddly enough). Since 'end' is 228 // already a clamped value, that case is handled here. 229 if (end == start) { 230 return; 231 } 232 233 // At this point we know for sure that end > start. 234 int length = count - end; 235 if (length >= 0) { 236 if (!shared) { 237 System.arraycopy(value, end, value, start, length); 238 } else { 239 char[] newData = new char[value.length]; 240 System.arraycopy(value, 0, newData, 0, start); 241 System.arraycopy(value, end, newData, start, length); 242 value = newData; 243 shared = false; 244 } 245 } 246 count -= end - start; 247 } 248 deleteCharAt0(int index)249 final void deleteCharAt0(int index) { 250 if (index < 0 || index >= count) { 251 throw indexAndLength(index); 252 } 253 254 delete0(index, index + 1); 255 } 256 257 /** 258 * Ensures that this object has a minimum capacity available before 259 * requiring the internal buffer to be enlarged. The general policy of this 260 * method is that if the {@code minimumCapacity} is larger than the current 261 * {@link #capacity()}, then the capacity will be increased to the largest 262 * value of either the {@code minimumCapacity} or the current capacity 263 * multiplied by two plus two. Although this is the general policy, there is 264 * no guarantee that the capacity will change. 265 * 266 * @param min 267 * the new minimum capacity to set. 268 */ ensureCapacity(int min)269 public void ensureCapacity(int min) { 270 if (min > value.length) { 271 int ourMin = value.length*2 + 2; 272 enlargeBuffer(Math.max(ourMin, min)); 273 } 274 } 275 276 /** 277 * Copies the requested sequence of characters into {@code dst} passed 278 * starting at {@code dst}. 279 * 280 * @param start 281 * the inclusive start index of the characters to copy. 282 * @param end 283 * the exclusive end index of the characters to copy. 284 * @param dst 285 * the {@code char[]} to copy the characters to. 286 * @param dstStart 287 * the inclusive start index of {@code dst} to begin copying to. 288 * @throws IndexOutOfBoundsException 289 * if the {@code start} is negative, the {@code dstStart} is 290 * negative, the {@code start} is greater than {@code end}, the 291 * {@code end} is greater than the current {@link #length()} or 292 * {@code dstStart + end - begin} is greater than 293 * {@code dst.length}. 294 */ getChars(int start, int end, char[] dst, int dstStart)295 public void getChars(int start, int end, char[] dst, int dstStart) { 296 if (start > count || end > count || start > end) { 297 throw startEndAndLength(start, end); 298 } 299 System.arraycopy(value, start, dst, dstStart, end - start); 300 } 301 insert0(int index, char[] chars)302 final void insert0(int index, char[] chars) { 303 if (index < 0 || index > count) { 304 throw indexAndLength(index); 305 } 306 if (chars.length != 0) { 307 move(chars.length, index); 308 System.arraycopy(chars, 0, value, index, chars.length); 309 count += chars.length; 310 } 311 } 312 insert0(int index, char[] chars, int start, int length)313 final void insert0(int index, char[] chars, int start, int length) { 314 if (index >= 0 && index <= count) { 315 // start + length could overflow, start/length maybe MaxInt 316 if (start >= 0 && length >= 0 && length <= chars.length - start) { 317 if (length != 0) { 318 move(length, index); 319 System.arraycopy(chars, start, value, index, length); 320 count += length; 321 } 322 return; 323 } 324 } 325 throw new StringIndexOutOfBoundsException("this.length=" + count 326 + "; index=" + index + "; chars.length=" + chars.length 327 + "; start=" + start + "; length=" + length); 328 } 329 insert0(int index, char ch)330 final void insert0(int index, char ch) { 331 if (index < 0 || index > count) { 332 // RI compatible exception type 333 throw new ArrayIndexOutOfBoundsException(count, index); 334 } 335 move(1, index); 336 value[index] = ch; 337 count++; 338 } 339 insert0(int index, String string)340 final void insert0(int index, String string) { 341 if (index >= 0 && index <= count) { 342 if (string == null) { 343 string = "null"; 344 } 345 int min = string.length(); 346 if (min != 0) { 347 move(min, index); 348 string._getChars(0, min, value, index); 349 count += min; 350 } 351 } else { 352 throw indexAndLength(index); 353 } 354 } 355 insert0(int index, CharSequence s, int start, int end)356 final void insert0(int index, CharSequence s, int start, int end) { 357 if (s == null) { 358 s = "null"; 359 } 360 if ((index | start | end) < 0 || index > count || start > end || end > s.length()) { 361 throw new IndexOutOfBoundsException(); 362 } 363 insert0(index, s.subSequence(start, end).toString()); 364 } 365 366 /** 367 * The current length. 368 * 369 * @return the number of characters contained in this instance. 370 */ length()371 public int length() { 372 return count; 373 } 374 move(int size, int index)375 private void move(int size, int index) { 376 int newCount; 377 if (value.length - count >= size) { 378 if (!shared) { 379 // index == count case is no-op 380 System.arraycopy(value, index, value, index + size, count - index); 381 return; 382 } 383 newCount = value.length; 384 } else { 385 newCount = Math.max(count + size, value.length*2 + 2); 386 } 387 388 char[] newData = new char[newCount]; 389 System.arraycopy(value, 0, newData, 0, index); 390 // index == count case is no-op 391 System.arraycopy(value, index, newData, index + size, count - index); 392 value = newData; 393 shared = false; 394 } 395 replace0(int start, int end, String string)396 final void replace0(int start, int end, String string) { 397 if (start >= 0) { 398 if (end > count) { 399 end = count; 400 } 401 if (end > start) { 402 int stringLength = string.length(); 403 int diff = end - start - stringLength; 404 if (diff > 0) { // replacing with fewer characters 405 if (!shared) { 406 // index == count case is no-op 407 System.arraycopy(value, end, value, start 408 + stringLength, count - end); 409 } else { 410 char[] newData = new char[value.length]; 411 System.arraycopy(value, 0, newData, 0, start); 412 // index == count case is no-op 413 System.arraycopy(value, end, newData, start 414 + stringLength, count - end); 415 value = newData; 416 shared = false; 417 } 418 } else if (diff < 0) { 419 // replacing with more characters...need some room 420 move(-diff, end); 421 } else if (shared) { 422 value = value.clone(); 423 shared = false; 424 } 425 string._getChars(0, stringLength, value, start); 426 count -= diff; 427 return; 428 } 429 if (start == end) { 430 if (string == null) { 431 throw new NullPointerException("string == null"); 432 } 433 insert0(start, string); 434 return; 435 } 436 } 437 throw startEndAndLength(start, end); 438 } 439 reverse0()440 final void reverse0() { 441 if (count < 2) { 442 return; 443 } 444 if (!shared) { 445 int end = count - 1; 446 char frontHigh = value[0]; 447 char endLow = value[end]; 448 boolean allowFrontSur = true, allowEndSur = true; 449 for (int i = 0, mid = count / 2; i < mid; i++, --end) { 450 char frontLow = value[i + 1]; 451 char endHigh = value[end - 1]; 452 boolean surAtFront = allowFrontSur && frontLow >= 0xdc00 453 && frontLow <= 0xdfff && frontHigh >= 0xd800 454 && frontHigh <= 0xdbff; 455 if (surAtFront && (count < 3)) { 456 return; 457 } 458 boolean surAtEnd = allowEndSur && endHigh >= 0xd800 459 && endHigh <= 0xdbff && endLow >= 0xdc00 460 && endLow <= 0xdfff; 461 allowFrontSur = allowEndSur = true; 462 if (surAtFront == surAtEnd) { 463 if (surAtFront) { 464 // both surrogates 465 value[end] = frontLow; 466 value[end - 1] = frontHigh; 467 value[i] = endHigh; 468 value[i + 1] = endLow; 469 frontHigh = value[i + 2]; 470 endLow = value[end - 2]; 471 i++; 472 end--; 473 } else { 474 // neither surrogates 475 value[end] = frontHigh; 476 value[i] = endLow; 477 frontHigh = frontLow; 478 endLow = endHigh; 479 } 480 } else { 481 if (surAtFront) { 482 // surrogate only at the front 483 value[end] = frontLow; 484 value[i] = endLow; 485 endLow = endHigh; 486 allowFrontSur = false; 487 } else { 488 // surrogate only at the end 489 value[end] = frontHigh; 490 value[i] = endHigh; 491 frontHigh = frontLow; 492 allowEndSur = false; 493 } 494 } 495 } 496 if ((count & 1) == 1 && (!allowFrontSur || !allowEndSur)) { 497 value[end] = allowFrontSur ? endLow : frontHigh; 498 } 499 } else { 500 char[] newData = new char[value.length]; 501 for (int i = 0, end = count; i < count; i++) { 502 char high = value[i]; 503 if ((i + 1) < count && high >= 0xd800 && high <= 0xdbff) { 504 char low = value[i + 1]; 505 if (low >= 0xdc00 && low <= 0xdfff) { 506 newData[--end] = low; 507 i++; 508 } 509 } 510 newData[--end] = high; 511 } 512 value = newData; 513 shared = false; 514 } 515 } 516 517 /** 518 * Sets the character at the {@code index}. 519 * 520 * @param index 521 * the zero-based index of the character to replace. 522 * @param ch 523 * the character to set. 524 * @throws IndexOutOfBoundsException 525 * if {@code index} is negative or greater than or equal to the 526 * current {@link #length()}. 527 */ setCharAt(int index, char ch)528 public void setCharAt(int index, char ch) { 529 if (index < 0 || index >= count) { 530 throw indexAndLength(index); 531 } 532 if (shared) { 533 value = value.clone(); 534 shared = false; 535 } 536 value[index] = ch; 537 } 538 539 /** 540 * Sets the current length to a new value. If the new length is larger than 541 * the current length, then the new characters at the end of this object 542 * will contain the {@code char} value of {@code \u0000}. 543 * 544 * @param length 545 * the new length of this StringBuffer. 546 * @throws IndexOutOfBoundsException 547 * if {@code length < 0}. 548 * @see #length 549 */ setLength(int length)550 public void setLength(int length) { 551 if (length < 0) { 552 throw new StringIndexOutOfBoundsException("length < 0: " + length); 553 } 554 if (length > value.length) { 555 enlargeBuffer(length); 556 } else { 557 if (shared) { 558 char[] newData = new char[value.length]; 559 System.arraycopy(value, 0, newData, 0, count); 560 value = newData; 561 shared = false; 562 } else { 563 if (count < length) { 564 Arrays.fill(value, count, length, (char) 0); 565 } 566 } 567 } 568 count = length; 569 } 570 571 /** 572 * Returns the String value of the subsequence from the {@code start} index 573 * to the current end. 574 * 575 * @param start 576 * the inclusive start index to begin the subsequence. 577 * @return a String containing the subsequence. 578 * @throws StringIndexOutOfBoundsException 579 * if {@code start} is negative or greater than the current 580 * {@link #length()}. 581 */ substring(int start)582 public String substring(int start) { 583 if (start >= 0 && start <= count) { 584 if (start == count) { 585 return ""; 586 } 587 588 // Remove String sharing for more performance 589 return new String(value, start, count - start); 590 } 591 throw indexAndLength(start); 592 } 593 594 /** 595 * Returns the String value of the subsequence from the {@code start} index 596 * to the {@code end} index. 597 * 598 * @param start 599 * the inclusive start index to begin the subsequence. 600 * @param end 601 * the exclusive end index to end the subsequence. 602 * @return a String containing the subsequence. 603 * @throws StringIndexOutOfBoundsException 604 * if {@code start} is negative, greater than {@code end} or if 605 * {@code end} is greater than the current {@link #length()}. 606 */ substring(int start, int end)607 public String substring(int start, int end) { 608 if (start >= 0 && start <= end && end <= count) { 609 if (start == end) { 610 return ""; 611 } 612 613 // Remove String sharing for more performance 614 return new String(value, start, end - start); 615 } 616 throw startEndAndLength(start, end); 617 } 618 619 /** 620 * Returns the current String representation. 621 * 622 * @return a String containing the characters in this instance. 623 */ 624 @Override toString()625 public String toString() { 626 if (count == 0) { 627 return ""; 628 } 629 // Optimize String sharing for more performance 630 int wasted = value.length - count; 631 if (wasted >= 256 632 || (wasted >= INITIAL_CAPACITY && wasted >= (count >> 1))) { 633 return new String(value, 0, count); 634 } 635 shared = true; 636 return new String(0, count, value); 637 } 638 639 /** 640 * Returns a {@code CharSequence} of the subsequence from the {@code start} 641 * index to the {@code end} index. 642 * 643 * @param start 644 * the inclusive start index to begin the subsequence. 645 * @param end 646 * the exclusive end index to end the subsequence. 647 * @return a CharSequence containing the subsequence. 648 * @throws IndexOutOfBoundsException 649 * if {@code start} is negative, greater than {@code end} or if 650 * {@code end} is greater than the current {@link #length()}. 651 * @since 1.4 652 */ subSequence(int start, int end)653 public CharSequence subSequence(int start, int end) { 654 return substring(start, end); 655 } 656 657 /** 658 * Searches for the first index of the specified character. The search for 659 * the character starts at the beginning and moves towards the end. 660 * 661 * @param string 662 * the string to find. 663 * @return the index of the specified character, -1 if the character isn't 664 * found. 665 * @see #lastIndexOf(String) 666 * @since 1.4 667 */ indexOf(String string)668 public int indexOf(String string) { 669 return indexOf(string, 0); 670 } 671 672 /** 673 * Searches for the index of the specified character. The search for the 674 * character starts at the specified offset and moves towards the end. 675 * 676 * @param subString 677 * the string to find. 678 * @param start 679 * the starting offset. 680 * @return the index of the specified character, -1 if the character isn't 681 * found 682 * @see #lastIndexOf(String,int) 683 * @since 1.4 684 */ indexOf(String subString, int start)685 public int indexOf(String subString, int start) { 686 if (start < 0) { 687 start = 0; 688 } 689 int subCount = subString.length(); 690 if (subCount > 0) { 691 if (subCount + start > count) { 692 return -1; 693 } 694 // TODO optimize charAt to direct array access 695 char firstChar = subString.charAt(0); 696 while (true) { 697 int i = start; 698 boolean found = false; 699 for (; i < count; i++) { 700 if (value[i] == firstChar) { 701 found = true; 702 break; 703 } 704 } 705 if (!found || subCount + i > count) { 706 return -1; // handles subCount > count || start >= count 707 } 708 int o1 = i, o2 = 0; 709 while (++o2 < subCount && value[++o1] == subString.charAt(o2)) { 710 // Intentionally empty 711 } 712 if (o2 == subCount) { 713 return i; 714 } 715 start = i + 1; 716 } 717 } 718 return (start < count || start == 0) ? start : count; 719 } 720 721 /** 722 * Searches for the last index of the specified character. The search for 723 * the character starts at the end and moves towards the beginning. 724 * 725 * @param string 726 * the string to find. 727 * @return the index of the specified character, -1 if the character isn't 728 * found. 729 * @throws NullPointerException 730 * if {@code string} is {@code null}. 731 * @see String#lastIndexOf(java.lang.String) 732 * @since 1.4 733 */ lastIndexOf(String string)734 public int lastIndexOf(String string) { 735 return lastIndexOf(string, count); 736 } 737 738 /** 739 * Searches for the index of the specified character. The search for the 740 * character starts at the specified offset and moves towards the beginning. 741 * 742 * @param subString 743 * the string to find. 744 * @param start 745 * the starting offset. 746 * @return the index of the specified character, -1 if the character isn't 747 * found. 748 * @throws NullPointerException 749 * if {@code subString} is {@code null}. 750 * @see String#lastIndexOf(String,int) 751 * @since 1.4 752 */ lastIndexOf(String subString, int start)753 public int lastIndexOf(String subString, int start) { 754 int subCount = subString.length(); 755 if (subCount <= count && start >= 0) { 756 if (subCount > 0) { 757 if (start > count - subCount) { 758 start = count - subCount; // count and subCount are both 759 } 760 // >= 1 761 // TODO optimize charAt to direct array access 762 char firstChar = subString.charAt(0); 763 while (true) { 764 int i = start; 765 boolean found = false; 766 for (; i >= 0; --i) { 767 if (value[i] == firstChar) { 768 found = true; 769 break; 770 } 771 } 772 if (!found) { 773 return -1; 774 } 775 int o1 = i, o2 = 0; 776 while (++o2 < subCount 777 && value[++o1] == subString.charAt(o2)) { 778 // Intentionally empty 779 } 780 if (o2 == subCount) { 781 return i; 782 } 783 start = i - 1; 784 } 785 } 786 return start < count ? start : count; 787 } 788 return -1; 789 } 790 791 /** 792 * Trims off any extra capacity beyond the current length. Note, this method 793 * is NOT guaranteed to change the capacity of this object. 794 * 795 * @since 1.5 796 */ trimToSize()797 public void trimToSize() { 798 if (count < value.length) { 799 char[] newValue = new char[count]; 800 System.arraycopy(value, 0, newValue, 0, count); 801 value = newValue; 802 shared = false; 803 } 804 } 805 806 /** 807 * Retrieves the Unicode code point value at the {@code index}. 808 * 809 * @param index 810 * the index to the {@code char} code unit. 811 * @return the Unicode code point value. 812 * @throws IndexOutOfBoundsException 813 * if {@code index} is negative or greater than or equal to 814 * {@link #length()}. 815 * @see Character 816 * @see Character#codePointAt(char[], int, int) 817 * @since 1.5 818 */ codePointAt(int index)819 public int codePointAt(int index) { 820 if (index < 0 || index >= count) { 821 throw indexAndLength(index); 822 } 823 return Character.codePointAt(value, index, count); 824 } 825 826 /** 827 * Retrieves the Unicode code point value that precedes the {@code index}. 828 * 829 * @param index 830 * the index to the {@code char} code unit within this object. 831 * @return the Unicode code point value. 832 * @throws IndexOutOfBoundsException 833 * if {@code index} is less than 1 or greater than 834 * {@link #length()}. 835 * @see Character 836 * @see Character#codePointBefore(char[], int, int) 837 * @since 1.5 838 */ codePointBefore(int index)839 public int codePointBefore(int index) { 840 if (index < 1 || index > count) { 841 throw indexAndLength(index); 842 } 843 return Character.codePointBefore(value, index); 844 } 845 846 /** 847 * Calculates the number of Unicode code points between {@code start} 848 * and {@code end}. 849 * 850 * @param start 851 * the inclusive beginning index of the subsequence. 852 * @param end 853 * the exclusive end index of the subsequence. 854 * @return the number of Unicode code points in the subsequence. 855 * @throws IndexOutOfBoundsException 856 * if {@code start} is negative or greater than 857 * {@code end} or {@code end} is greater than 858 * {@link #length()}. 859 * @see Character 860 * @see Character#codePointCount(char[], int, int) 861 * @since 1.5 862 */ codePointCount(int start, int end)863 public int codePointCount(int start, int end) { 864 if (start < 0 || end > count || start > end) { 865 throw startEndAndLength(start, end); 866 } 867 return Character.codePointCount(value, start, end - start); 868 } 869 870 /** 871 * Returns the index that is offset {@code codePointOffset} code points from 872 * {@code index}. 873 * 874 * @param index 875 * the index to calculate the offset from. 876 * @param codePointOffset 877 * the number of code points to count. 878 * @return the index that is {@code codePointOffset} code points away from 879 * index. 880 * @throws IndexOutOfBoundsException 881 * if {@code index} is negative or greater than 882 * {@link #length()} or if there aren't enough code points 883 * before or after {@code index} to match 884 * {@code codePointOffset}. 885 * @see Character 886 * @see Character#offsetByCodePoints(char[], int, int, int, int) 887 * @since 1.5 888 */ offsetByCodePoints(int index, int codePointOffset)889 public int offsetByCodePoints(int index, int codePointOffset) { 890 return Character.offsetByCodePoints(value, 0, count, index, 891 codePointOffset); 892 } 893 } 894