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