• 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 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