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