001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.lang;
019:
020: import java.io.InvalidObjectException;
021: import java.util.Arrays;
022:
023: import org.apache.harmony.luni.util.Msg;
024:
025: /**
026: * A modifiable {@link CharSequence sequence of characters} for use in creating
027: * and modifying Strings. This class is intended as a base class for
028: * {@link java.lang.StringBuffer} and {@link java.lang.StringBuilder}.
029: *
030: * @see java.lang.StringBuffer
031: * @see java.lang.StringBuilder
032: *
033: * @since 1.5
034: */
035: abstract class AbstractStringBuilder {
036:
037: static final int INITIAL_CAPACITY = 16;
038:
039: private char[] value;
040:
041: private int count;
042:
043: private boolean shared;
044:
045: /*
046: * Returns the character array.
047: */
048: final char[] getValue() {
049: return value;
050: }
051:
052: /*
053: * Returns the underlying buffer and sets the shared flag.
054: */
055: final char[] shareValue() {
056: shared = true;
057: return value;
058: }
059:
060: /*
061: * Restores internal state after deserialization.
062: */
063: final void set(char[] val, int len) throws InvalidObjectException {
064: if (val == null) {
065: val = new char[0];
066: }
067: if (val.length < len) {
068: throw new InvalidObjectException(Msg.getString("K0199")); //$NON-NLS-1$
069: }
070:
071: shared = false;
072: value = val;
073: count = len;
074: }
075:
076: AbstractStringBuilder() {
077: value = new char[INITIAL_CAPACITY];
078: }
079:
080: AbstractStringBuilder(int capacity) {
081: if (capacity < 0) {
082: throw new NegativeArraySizeException();
083: }
084: value = new char[capacity];
085: }
086:
087: AbstractStringBuilder(String string) {
088: count = string.length();
089: shared = false;
090: value = new char[count + INITIAL_CAPACITY];
091: string.getChars(0, count, value, 0);
092: }
093:
094: private void enlargeBuffer(int min) {
095: int twice = (value.length << 1) + 2;
096: char[] newData = new char[min > twice ? min : twice];
097: System.arraycopy(value, 0, newData, 0, count);
098: value = newData;
099: shared = false;
100: }
101:
102: final void appendNull() {
103: int newSize = count + 4;
104: if (newSize > value.length) {
105: enlargeBuffer(newSize);
106: } else if (shared) {
107: value = value.clone();
108: shared = false;
109: }
110: value[count++] = 'n';
111: value[count++] = 'u';
112: value[count++] = 'l';
113: value[count++] = 'l';
114: }
115:
116: final void append0(char chars[]) {
117: int newSize = count + chars.length;
118: if (newSize > value.length) {
119: enlargeBuffer(newSize);
120: } else if (shared) {
121: value = value.clone();
122: shared = false;
123: }
124: System.arraycopy(chars, 0, value, count, chars.length);
125: count = newSize;
126: }
127:
128: final void append0(char chars[], int start, int length) {
129: if (chars == null) {
130: throw new NullPointerException();
131: }
132: // start + length could overflow, start/length maybe MaxInt
133: if (start >= 0 && 0 <= length && length <= chars.length - start) {
134: int newSize = count + length;
135: if (newSize > value.length) {
136: enlargeBuffer(newSize);
137: } else if (shared) {
138: value = value.clone();
139: shared = false;
140: }
141: System.arraycopy(chars, start, value, count, length);
142: count = newSize;
143: } else {
144: throw new ArrayIndexOutOfBoundsException();
145: }
146: }
147:
148: final void append0(char ch) {
149: if (count == value.length) {
150: enlargeBuffer(count + 1);
151: }
152: if (shared) {
153: value = value.clone();
154: shared = false;
155: }
156: value[count++] = ch;
157: }
158:
159: final void append0(String string) {
160: if (string == null) {
161: appendNull();
162: return;
163: }
164: int adding = string.length();
165: int newSize = count + adding;
166: if (newSize > value.length) {
167: enlargeBuffer(newSize);
168: } else if (shared) {
169: value = value.clone();
170: shared = false;
171: }
172: string.getChars(0, adding, value, count);
173: count = newSize;
174: }
175:
176: final void append0(CharSequence s, int start, int end) {
177: if (s == null) {
178: s = "null"; //$NON-NLS-1$
179: }
180: if (start < 0 || end < 0 || start > end || end > s.length()) {
181: throw new IndexOutOfBoundsException();
182: }
183:
184: append0(s.subSequence(start, end).toString());
185: }
186:
187: /**
188: * Answers the number of characters this StringBuffer can hold without
189: * growing.
190: *
191: * @return the capacity of this StringBuffer
192: *
193: * @see #ensureCapacity
194: * @see #length
195: */
196: public int capacity() {
197: return value.length;
198: }
199:
200: /**
201: * Retrieves the character at the <code>index</code>.
202: *
203: * @param index
204: * The index of character in this object to retrieve.
205: * @return The char value.
206: * @throws IndexOutOfBoundsException
207: * if <code>index</code> is negative or greater than or equal
208: * to the current {@link #length()}.
209: */
210: public char charAt(int index) {
211: if (index < 0 || index >= count) {
212: throw new StringIndexOutOfBoundsException(index);
213: }
214: return value[index];
215: }
216:
217: final void delete0(int start, int end) {
218: if (start >= 0) {
219: if (end > count) {
220: end = count;
221: }
222: if (end == start) {
223: return;
224: }
225: if (end > start) {
226: int length = count - end;
227: if (length > 0) {
228: if (!shared) {
229: System.arraycopy(value, end, value, start,
230: length);
231: } else {
232: char[] newData = new char[value.length];
233: System.arraycopy(value, 0, newData, 0, start);
234: System.arraycopy(value, end, newData, start,
235: length);
236: value = newData;
237: shared = false;
238: }
239: }
240: count -= end - start;
241: return;
242: }
243: }
244: throw new StringIndexOutOfBoundsException();
245: }
246:
247: final void deleteCharAt0(int location) {
248: if (0 > location || location >= count) {
249: throw new StringIndexOutOfBoundsException(location);
250: }
251: int length = count - location - 1;
252: if (length > 0) {
253: if (!shared) {
254: System.arraycopy(value, location + 1, value, location,
255: length);
256: } else {
257: char[] newData = new char[value.length];
258: System.arraycopy(value, 0, newData, 0, location);
259: System.arraycopy(value, location + 1, newData,
260: location, length);
261: value = newData;
262: shared = false;
263: }
264: }
265: count--;
266: }
267:
268: /**
269: * Ensures that this object has a minimum capacity available before
270: * requiring the internal buffer to be enlarged. The general policy of this
271: * method is that if the <code>minimumCapacity</code> is larger than the
272: * current {@link #capacity()}, then the capacity will be increased to the
273: * largest value of either the <code>minimumCapacity</code> or the current
274: * capacity multiplied by two plus two. Although this is the general policy,
275: * there is no guarantee that the capacity will change.
276: *
277: * @param min
278: * The new minimum capacity to set.
279: */
280: public void ensureCapacity(int min) {
281: if (min > value.length) {
282: enlargeBuffer(min);
283: }
284: }
285:
286: /**
287: * Copies the requested sequence of characters to be copied to the
288: * <code>char[]</code> passed.
289: *
290: * @param start
291: * The inclusive start index of the characters to copy from this
292: * object.
293: * @param end
294: * The exclusive end index of the characters to copy from this
295: * object.
296: * @param dest
297: * The <code>char[]</code> to copy the characters to.
298: * @param destStart
299: * The inclusive start index of the <code>dest</code> parameter
300: * to begin copying to.
301: * @throws IndexOutOfBoundsException
302: * if the <code>start</code> is negative, the
303: * <code>destStart</code> is negative, the <code>start</code>
304: * is greater than <code>end</code>, the <code>end</code>
305: * is greater than the current {@link #length()} or
306: * <code>destStart + end - begin</code> is greater than
307: * <code>dest.length</code>.
308: */
309: public void getChars(int start, int end, char[] dest, int destStart) {
310: if (start > count || end > count || start > end) {
311: throw new StringIndexOutOfBoundsException();
312: }
313: System.arraycopy(value, start, dest, destStart, end - start);
314: }
315:
316: final void insert0(int index, char[] chars) {
317: if (0 > index || index > count) {
318: throw new StringIndexOutOfBoundsException(index);
319: }
320: if (chars.length != 0) {
321: move(chars.length, index);
322: System.arraycopy(chars, 0, value, index, chars.length);
323: count += chars.length;
324: }
325: }
326:
327: final void insert0(int index, char[] chars, int start, int length) {
328: if (0 <= index && index <= count) {
329: // start + length could overflow, start/length maybe MaxInt
330: if (start >= 0 && 0 <= length
331: && length <= chars.length - start) {
332: if (length != 0) {
333: move(length, index);
334: System
335: .arraycopy(chars, start, value, index,
336: length);
337: count += length;
338: }
339: return;
340: }
341: throw new StringIndexOutOfBoundsException("offset " + start //$NON-NLS-1$
342: + ", length " + length //$NON-NLS-1$
343: + ", char[].length " + chars.length); //$NON-NLS-1$
344: }
345: throw new StringIndexOutOfBoundsException(index);
346: }
347:
348: final void insert0(int index, char ch) {
349: if (0 > index || index > count) {
350: // RI compatible exception type
351: throw new ArrayIndexOutOfBoundsException(index);
352: }
353: move(1, index);
354: value[index] = ch;
355: count++;
356: }
357:
358: final void insert0(int index, String string) {
359: if (0 <= index && index <= count) {
360: if (string == null) {
361: string = "null"; //$NON-NLS-1$
362: }
363: int min = string.length();
364: if (min != 0) {
365: move(min, index);
366: string.getChars(0, min, value, index);
367: count += min;
368: }
369: } else {
370: throw new StringIndexOutOfBoundsException(index);
371: }
372: }
373:
374: final void insert0(int index, CharSequence s, int start, int end) {
375: if (s == null) {
376: s = "null"; //$NON-NLS-1$
377: }
378: if (index < 0 || index > count || start < 0 || end < 0
379: || start > end || end > s.length()) {
380: throw new IndexOutOfBoundsException();
381: }
382: insert0(index, s.subSequence(start, end).toString());
383: }
384:
385: /**
386: * The current length of this object.
387: *
388: * @return the number of characters in this StringBuffer
389: */
390: public int length() {
391: return count;
392: }
393:
394: private void move(int size, int index) {
395: int newSize;
396: if (value.length - count >= size) {
397: if (!shared) {
398: System.arraycopy(value, index, value, index + size,
399: count - index); // index == count case is no-op
400: return;
401: }
402: newSize = value.length;
403: } else {
404: int a = count + size, b = (value.length << 1) + 2;
405: newSize = a > b ? a : b;
406: }
407:
408: char[] newData = new char[newSize];
409: System.arraycopy(value, 0, newData, 0, index);
410: // index == count case is no-op
411: System.arraycopy(value, index, newData, index + size, count
412: - index);
413: value = newData;
414: shared = false;
415: }
416:
417: final void replace0(int start, int end, String string) {
418: if (start >= 0) {
419: if (end > count) {
420: end = count;
421: }
422: if (end > start) {
423: int stringLength = string.length();
424: int diff = end - start - stringLength;
425: if (diff > 0) { // replacing with fewer characters
426: if (!shared) {
427: // index == count case is no-op
428: System.arraycopy(value, end, value, start
429: + stringLength, count - end);
430: } else {
431: char[] newData = new char[value.length];
432: System.arraycopy(value, 0, newData, 0, start);
433: // index == count case is no-op
434: System.arraycopy(value, end, newData, start
435: + stringLength, count - end);
436: value = newData;
437: shared = false;
438: }
439: } else if (diff < 0) {
440: // replacing with more characters...need some room
441: move(-diff, end);
442: } else if (shared) {
443: value = value.clone();
444: shared = false;
445: }
446: string.getChars(0, stringLength, value, start);
447: count -= diff;
448: return;
449: }
450: if (start == end) {
451: if (string == null) {
452: throw new NullPointerException();
453: }
454: insert0(start, string);
455: return;
456: }
457: }
458: throw new StringIndexOutOfBoundsException();
459: }
460:
461: final void reverse0() {
462: if (count < 2) {
463: return;
464: }
465: if (!shared) {
466: for (int i = 0, end = count, mid = count / 2; i < mid; i++) {
467: char temp = value[--end];
468: value[end] = value[i];
469: value[i] = temp;
470: }
471: } else {
472: char[] newData = new char[value.length];
473: for (int i = 0, end = count; i < count; i++) {
474: newData[--end] = value[i];
475: }
476: value = newData;
477: shared = false;
478: }
479: }
480:
481: /**
482: * Sets the character at the <code>index</code> in this object.
483: *
484: * @param index
485: * the zero-based index of the character to replace.
486: * @param ch
487: * the character to set.
488: * @throws IndexOutOfBoundsException
489: * if <code>index</code> is negative or greater than or equal
490: * to the current {@link #length()}.
491: */
492: public void setCharAt(int index, char ch) {
493: if (0 > index || index >= count) {
494: throw new StringIndexOutOfBoundsException(index);
495: }
496: if (shared) {
497: value = value.clone();
498: shared = false;
499: }
500: value[index] = ch;
501: }
502:
503: /**
504: * Sets the current length to a new value. If the new length is larger than
505: * the current length, then the new characters at the end of this object
506: * will contain the <code>char</code> value of <code>\u0000</code>.
507: *
508: * @param length
509: * the new length of this StringBuffer
510: *
511: * @exception IndexOutOfBoundsException
512: * when <code>length < 0</code>
513: *
514: * @see #length
515: */
516: public void setLength(int length) {
517: if (length < 0) {
518: throw new StringIndexOutOfBoundsException(length);
519: }
520: if (count < length) {
521: if (length > value.length) {
522: enlargeBuffer(length);
523: } else {
524: if (shared) {
525: char[] newData = new char[value.length];
526: System.arraycopy(value, 0, newData, 0, count);
527: value = newData;
528: shared = false;
529: } else {
530: Arrays.fill(value, count, length, (char) 0);
531: }
532: }
533: }
534: count = length;
535: }
536:
537: /**
538: * Returns the String value of the subsequence of this object from the
539: * <code>start</code> index to the current end.
540: *
541: * @param start
542: * The inclusive start index to begin the subsequence.
543: * @return A String containing the subsequence.
544: * @throws StringIndexOutOfBoundsException
545: * if <code>start</code> is negative or greater than the
546: * current {@link #length()}.
547: */
548: public String substring(int start) {
549: if (0 <= start && start <= count) {
550: if (start == count) {
551: return ""; //$NON-NLS-1$
552: }
553:
554: shared = true;
555: return new String(start, count - start, value);
556: }
557: throw new StringIndexOutOfBoundsException(start);
558: }
559:
560: /**
561: * Returns the String value of the subsequence of this object from the
562: * <code>start</code> index to the <code>start</code> index.
563: *
564: * @param start
565: * The inclusive start index to begin the subsequence.
566: * @param end
567: * The exclusive end index to end the subsequence.
568: * @return A String containing the subsequence.
569: * @throws StringIndexOutOfBoundsException
570: * if <code>start</code> is negative, greater than the current
571: * {@link #length()} or greater than <code>end</code>.
572: */
573: public String substring(int start, int end) {
574: if (0 <= start && start <= end && end <= count) {
575: if (start == end) {
576: return ""; //$NON-NLS-1$
577: }
578:
579: shared = true;
580: return new String(value, start, end - start);
581: }
582: throw new StringIndexOutOfBoundsException();
583: }
584:
585: /**
586: * Returns the current String representation of this object.
587: *
588: * @return a String containing the characters in this StringBuilder.
589: */
590: @Override
591: public String toString() {
592: if (count == 0) {
593: return ""; //$NON-NLS-1$
594: }
595:
596: if (count >= 256 && count <= (value.length >> 1)) {
597: return new String(value, 0, count);
598: }
599: shared = true;
600: return new String(0, count, value);
601: }
602:
603: /**
604: * Returns a <code>CharSequence</code> of the subsequence of this object
605: * from the <code>start</code> index to the <code>start</code> 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 CharSequence containing the subsequence.
612: * @throws IndexOutOfBoundsException
613: * if <code>start</code> is negative, greater than the current
614: * {@link #length()} or greater than <code>end</code>.
615: *
616: * @since 1.4
617: */
618: public CharSequence subSequence(int start, int end) {
619: return substring(start, end);
620: }
621:
622: /**
623: * Searches in this StringBuffer for the first index of the specified
624: * character. The search for the character starts at the beginning and moves
625: * towards the end.
626: *
627: * @param string
628: * the string to find
629: * @return the index in this StringBuffer of the specified character, -1 if
630: * the character isn't found
631: *
632: * @see #lastIndexOf(String)
633: *
634: * @since 1.4
635: */
636: public int indexOf(String string) {
637: return indexOf(string, 0);
638: }
639:
640: /**
641: * Searches in this StringBuffer for the index of the specified character.
642: * The search for the character starts at the specified offset and moves
643: * towards the end.
644: *
645: * @param subString
646: * the string to find
647: * @param start
648: * the starting offset
649: * @return the index in this StringBuffer of the specified character, -1 if
650: * the character isn't found
651: *
652: * @see #lastIndexOf(String,int)
653: *
654: * @since 1.4
655: */
656: public int indexOf(String subString, int start) {
657: if (start < 0) {
658: start = 0;
659: }
660: int subCount = subString.length();
661: if (subCount > 0) {
662: if (subCount + start > count) {
663: return -1;
664: }
665: // TODO optimize charAt to direct array access
666: char firstChar = subString.charAt(0);
667: while (true) {
668: int i = start;
669: boolean found = false;
670: for (; i < count; i++) {
671: if (value[i] == firstChar) {
672: found = true;
673: break;
674: }
675: }
676: if (!found || subCount + i > count) {
677: return -1; // handles subCount > count || start >= count
678: }
679: int o1 = i, o2 = 0;
680: while (++o2 < subCount
681: && value[++o1] == subString.charAt(o2)) {
682: // Intentionally empty
683: }
684: if (o2 == subCount) {
685: return i;
686: }
687: start = i + 1;
688: }
689: }
690: return (start < count || start == 0) ? start : count;
691: }
692:
693: /**
694: * Searches in this StringBuffer for the last index of the specified
695: * character. The search for the character starts at the end and moves
696: * towards the beginning.
697: *
698: * @param string
699: * the string to find
700: * @return the index in this StringBuffer of the specified character, -1 if
701: * the character isn't found
702: * @throws NullPointerException
703: * if the <code>string</code> parameter is <code>null</code>.
704: *
705: * @see String#lastIndexOf(java.lang.String)
706: *
707: * @since 1.4
708: */
709: public int lastIndexOf(String string) {
710: return lastIndexOf(string, count);
711: }
712:
713: /**
714: * Searches in this StringBuffer for the index of the specified character.
715: * The search for the character starts at the specified offset and moves
716: * towards the beginning.
717: *
718: * @param subString
719: * the string to find
720: * @param start
721: * the starting offset
722: * @return the index in this StringBuffer of the specified character, -1 if
723: * the character isn't found
724: * @throws NullPointerException
725: * if the <code>subString</code> parameter is
726: * <code>null</code>.
727: * @see String#lastIndexOf(java.lang.String,int)
728: * @since 1.4
729: */
730: public int lastIndexOf(String subString, int start) {
731: int subCount = subString.length();
732: if (subCount <= count && start >= 0) {
733: if (subCount > 0) {
734: if (start > count - subCount) {
735: start = count - subCount; // count and subCount are both
736: }
737: // >= 1
738: // TODO optimize charAt to direct array access
739: char firstChar = subString.charAt(0);
740: while (true) {
741: int i = start;
742: boolean found = false;
743: for (; i >= 0; --i) {
744: if (value[i] == firstChar) {
745: found = true;
746: break;
747: }
748: }
749: if (!found) {
750: return -1;
751: }
752: int o1 = i, o2 = 0;
753: while (++o2 < subCount
754: && value[++o1] == subString.charAt(o2)) {
755: // Intentionally empty
756: }
757: if (o2 == subCount) {
758: return i;
759: }
760: start = i - 1;
761: }
762: }
763: return start < count ? start : count;
764: }
765: return -1;
766: }
767:
768: /**
769: * Trims off any extra capacity beyond the current length. Note, this method
770: * is NOT guaranteed to change the capacity of this object.
771: *
772: * @since 1.5
773: */
774: public void trimToSize() {
775: if (count < value.length) {
776: char[] newValue = new char[count];
777: System.arraycopy(value, 0, newValue, 0, count);
778: value = newValue;
779: shared = false;
780: }
781: }
782:
783: /**
784: * Retrieves the Unicode code point value at the <code>index</code>.
785: *
786: * @param index
787: * The index to the <code>char</code> code unit within this
788: * object.
789: * @return The Unicode code point value.
790: * @throws IndexOutOfBoundsException
791: * if <code>index</code> is negative or greater than or equal
792: * to {@link #length()}.
793: * @see Character
794: * @see Character#codePointAt(char[], int, int)
795: * @since 1.5
796: */
797: public int codePointAt(int index) {
798: if (index < 0 || index >= count) {
799: throw new StringIndexOutOfBoundsException(index);
800: }
801: return Character.codePointAt(value, index, count);
802: }
803:
804: /**
805: * Retrieves the Unicode code point value that precedes the
806: * <code>index</code>.
807: *
808: * @param index
809: * The index to the <code>char</code> code unit within this
810: * object.
811: * @return The Unicode code point value.
812: * @throws IndexOutOfBoundsException
813: * if <code>index</code> is less than 1 or greater than
814: * {@link #length()}.
815: * @see Character
816: * @see Character#codePointBefore(char[], int, int)
817: * @since 1.5
818: */
819: public int codePointBefore(int index) {
820: if (index < 1 || index > count) {
821: throw new StringIndexOutOfBoundsException(index);
822: }
823: return Character.codePointBefore(value, index);
824: }
825:
826: /**
827: * Calculates the number of Unicode code points between
828: * <code>beginIndex</code> and <code>endIndex</code>.
829: *
830: * @param beginIndex
831: * The inclusive beginning index of the subsequence.
832: * @param endIndex
833: * The exclusive end index of the subsequence.
834: * @return The number of Unicode code points in the subsequence.
835: * @throws IndexOutOfBoundsException
836: * if <code>beginIndex</code> is negative or greater than
837: * <code>endIndex</code> or <code>endIndex</code> is greater
838: * than {@link #length()}.
839: * @since 1.5
840: */
841: public int codePointCount(int beginIndex, int endIndex) {
842: if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
843: throw new StringIndexOutOfBoundsException();
844: }
845: return Character.codePointCount(value, beginIndex, endIndex
846: - beginIndex);
847: }
848:
849: /**
850: * Returns the index within this object that is offset from
851: * <code>index</code> by <code>codePointOffset</code> code points.
852: *
853: * @param index
854: * The index within this object to calculate the offset from.
855: * @param codePointOffset
856: * The number of code points to count.
857: * @return The index within this object that is the offset.
858: * @throws IndexOutOfBoundsException
859: * if <code>index</code> is negative or greater than
860: * {@link #length()} or if there aren't enough code points
861: * before or after <code>index</code> to match
862: * <code>codePointOffset</code>.
863: * @since 1.5
864: */
865: public int offsetByCodePoints(int index, int codePointOffset) {
866: return Character.offsetByCodePoints(value, 0, count, index,
867: codePointOffset);
868: }
869: }
|