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