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