/* * @(#) $(JCGO)/goclsp/vm_str/java/lang/StringBuffer.java -- * Space-optimized implementation of the standard StringBuffer class. ** * Project: JCGO (http://www.ivmaisoft.com/jcgo/) * Copyright (C) 2001-2007 Ivan Maidanski <ivmai@ivmaisoft.com> * All rights reserved. ** * Class specification origin: GNU Classpath v0.93 */ /* * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. ** * This software is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License (GPL) for more details. ** * Linking this library statically or dynamically with other modules is * making a combined work based on this library. Thus, the terms and * conditions of the GNU General Public License cover the whole * combination. ** * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent * modules, and to copy and distribute the resulting executable under * terms of your choice, provided that you also meet, for each linked * independent module, the terms and conditions of the license of that * module. An independent module is a module which is not derived from * or based on this library. If you modify this library, you may extend * this exception to your version of the library, but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ package java.lang; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public final class StringBuffer /* hard-coded class name */ implements Serializable, CharSequence /* , Appendable */ { // StringBuilder class: replace all "StringBuffer" with "StringBuilder" // except for append(StringBuffer). // StringBuilder class: remove all "synchronized" for StringBuilder // except for that in append(StringBuffer). // StringBuilder class: serialVersionUID is 4383685877147921099L private static final long serialVersionUID = 3388685877147921107L; private static final int DEFAULT_CAPACITY = 16; int count; char[] value; boolean shared; private transient byte[] bytes; public StringBuffer() /* hard-coded method signature */ { this(DEFAULT_CAPACITY); } public StringBuffer(int capacity) { bytes = new byte[capacity]; } public StringBuffer(String str) { int count = str.length(); Object strValue = str.value; if (strValue instanceof byte[]) VMSystem.arraycopy((byte[]) strValue, str.offset, bytes = new byte[count + DEFAULT_CAPACITY], 0, count); else VMSystem.arraycopy((char[]) strValue, str.offset, value = new char[count + DEFAULT_CAPACITY], 0, count); this.count = count; } public StringBuffer(CharSequence seq) { int len = seq.length(); if (len > 0) { char ch = seq.charAt(0); int i = 0; byte[] newBytes = null; if (ch <= 0xff) { newBytes = new byte[len + DEFAULT_CAPACITY]; do { newBytes[i++] = (byte) ch; if (i >= len) break; ch = seq.charAt(i); } while (ch <= 0xff); } if (i < len) { char[] newChars = new char[len + DEFAULT_CAPACITY]; if (i > 0) copyBytesToChars(newBytes, 0, newChars, 0, i); do { newChars[i++] = ch; if (i >= len) break; ch = seq.charAt(i); } while (true); value = newChars; } else bytes = newBytes; count = len; } else bytes = new byte[DEFAULT_CAPACITY]; } public synchronized int length() { return count; } public synchronized int capacity() { byte[] bytes = this.bytes; return bytes != null ? bytes.length : value.length; } public synchronized void ensureCapacity(int minimumCapacity) { if (minimumCapacity <= 0) minimumCapacity = 0; ensureCapacityInner(minimumCapacity, false); } public synchronized void setLength(int newLength) { if (newLength < 0) throw new StringIndexOutOfBoundsException(newLength); byte[] bytes = this.bytes; char[] chars = value; ensureCapacityInner(newLength, false); int count = this.count; if (newLength > count && this.bytes == bytes && value == chars) { if (bytes != null) { do { bytes[count++] = 0; } while (count < newLength); } else { do { chars[count++] = 0; } while (count < newLength); } } this.count = newLength; } public synchronized char charAt(int index) { if (index < 0 || index >= count) throw new StringIndexOutOfBoundsException(index); byte[] bytes = this.bytes; return bytes != null ? (char) (bytes[index] & 0xff) : value[index]; } public synchronized int codePointAt(int index) { int count = this.count; if (index < 0 || index >= count) throw new StringIndexOutOfBoundsException(index); byte[] bytes = this.bytes; return bytes != null ? bytes[index] & 0xff : Character.codePointAt(value, index, count); } public synchronized int codePointBefore(int index) { if (index <= 0 || index > count) throw new StringIndexOutOfBoundsException(index); byte[] bytes = this.bytes; return bytes != null ? bytes[index - 1] & 0xff : Character.codePointBefore(value, index, 1); } public synchronized void getChars(int srcOffset, int srcEnd, char[] dest, int destOffset) { if (((count - srcEnd) | srcOffset) < 0 || srcEnd < srcOffset) throw new StringIndexOutOfBoundsException(); byte[] bytes = this.bytes; if (bytes != null) copyBytesToChars(bytes, srcOffset, dest, destOffset, srcEnd - srcOffset); else VMSystem.arraycopy(value, srcOffset, dest, destOffset, srcEnd - srcOffset); } public synchronized void setCharAt(int index, char ch) { if (index < 0 || index >= count) throw new StringIndexOutOfBoundsException(index); ensureCapacityInner(count, ch > 0xff); byte[] bytes = this.bytes; if (bytes != null) bytes[index] = (byte) ch; else value[index] = ch; } public StringBuffer append(Object obj) { return append(obj != null ? obj.toString() : "null"); } public synchronized StringBuffer append( String str) /* hard-coded method signature */ { if (str == null) str = "null"; int count = this.count; int len = str.length(); Object strValue = str.value; int strOffset = str.offset; byte[] strBytes = null; boolean outOfByte = false; if (len > 0) { if (strValue instanceof byte[]) strBytes = (byte[]) strValue; else if (this.bytes != null && hasHighByte((char[]) strValue, strOffset, len)) outOfByte = true; } ensureCapacityInner(count + len, outOfByte); if (len > 0) { byte[] bytes = this.bytes; if (bytes != null) { if (strBytes != null) VMSystem.arraycopy(strBytes, strOffset, bytes, count, len); else copyCharsToBytes((char[]) strValue, strOffset, bytes, count, len); } else { if (strBytes != null) copyBytesToChars(strBytes, strOffset, value, count, len); else VMSystem.arraycopy(strValue, strOffset, value, count, len); } this.count = count + len; } return this; } public synchronized StringBuffer append(StringBuffer sBuf) { if (sBuf == null) return append("null"); int count = this.count; synchronized (sBuf) { int len = sBuf.length(); ensureCapacityInner(count + len, len > 0 && !(sBuf.value() instanceof byte[])); if (len > 0) { byte[] bytes = this.bytes; Object sBufValue = sBuf.value(); if (bytes != null) VMSystem.arraycopy(sBufValue, 0, bytes, count, len); else { if (sBufValue instanceof byte[]) copyBytesToChars((byte[]) sBufValue, 0, value, count, len); else VMSystem.arraycopy(sBufValue, 0, value, count, len); } this.count = count + len; } } return this; } public StringBuffer append(CharSequence seq) { if (seq == null) seq = "null"; return append(seq, 0, seq.length()); } public synchronized StringBuffer append(CharSequence seq, int start, int end) { if (seq == null) return append("null"); if (end - start > 0) { ensureCapacityInner(count + end - start, false); while (start < end) { char ch = seq.charAt(start++); int count = this.count; ensureCapacityInner(count + 1, ch > 0xff); byte[] bytes = this.bytes; if (bytes != null) bytes[count] = (byte) ch; else value[count] = ch; this.count = count + 1; } } return this; } public synchronized StringBuffer append(char[] data) { int count = data.length; int oldCount = this.count; ensureCapacityInner(oldCount + count, hasHighByte(data, 0, count)); byte[] bytes = this.bytes; if (bytes != null) copyCharsToBytes(data, 0, bytes, oldCount, count); else VMSystem.arraycopy(data, 0, value, oldCount, count); this.count = oldCount + count; return this; } public synchronized StringBuffer append(char[] data, int offset, int count) { if (data.length - count < offset || (offset | count) < 0) throw new StringIndexOutOfBoundsException(); int oldCount = this.count; ensureCapacityInner(oldCount + count, hasHighByte(data, offset, count)); byte[] bytes = this.bytes; if (bytes != null) copyCharsToBytes(data, offset, bytes, oldCount, count); else VMSystem.arraycopy(data, offset, value, oldCount, count); this.count = oldCount + count; return this; } public StringBuffer append(boolean bool) { return append(bool ? "true" : "false"); } public synchronized StringBuffer append(char ch) { int count = this.count; ensureCapacityInner(count + 1, ch > 0xff); byte[] bytes = this.bytes; if (bytes != null) bytes[count] = (byte) ch; else value[count] = ch; this.count = count + 1; return this; } public StringBuffer append(int v) { return append(String.valueOf(v)); } public StringBuffer append(long v) { return append(Long.toString(v, 10)); } public StringBuffer append(float v) { return append(Float.toString(v)); } public StringBuffer append(double v) { return append(Double.toString(v)); } public synchronized StringBuffer appendCodePoint(int code) { int len = Character.charCount(code); int count = this.count; ensureCapacityInner(count + len, code > 0xff); byte[] bytes = this.bytes; if (bytes != null) bytes[count] = (byte) code; else Character.toChars(code, value, count); this.count = count + len; return this; } public synchronized StringBuffer delete(int start, int end) { int count = this.count; if (((count - start) | start) < 0 || start > end) throw new StringIndexOutOfBoundsException(start); ensureCapacityInner(count, false); int len = 0; if (count > end) { len = count - end; byte[] bytes = this.bytes; if (bytes != null) VMSystem.arraycopy(bytes, end, bytes, start, len); else { char[] chars = value; VMSystem.arraycopy(chars, end, chars, start, len); } } this.count = start + len; return this; } public StringBuffer deleteCharAt(int index) { return delete(index, index + 1); } public synchronized StringBuffer replace(int start, int end, String str) { int len = str.length(); int count = this.count; if (((count - start) | start) < 0 || start > end) throw new StringIndexOutOfBoundsException(start); int delta = len - (end < count ? end : count) + start; Object strValue = str.value; byte[] strBytes = null; if (strValue instanceof byte[]) strBytes = (byte[]) strValue; ensureCapacityInner(count + delta, len > 0 && strBytes == null); int strOffset = str.offset; byte[] bytes = this.bytes; if (bytes != null) { if (delta != 0 && end < count) VMSystem.arraycopy(bytes, end, bytes, end + delta, count - end); if (len > 0) VMSystem.arraycopy(strBytes, strOffset, bytes, start, len); } else { char[] chars = value; if (delta != 0 && end < count) VMSystem.arraycopy(chars, end, chars, end + delta, count - end); if (len > 0) { if (strBytes != null) copyBytesToChars(strBytes, strOffset, chars, start, len); else VMSystem.arraycopy(strValue, strOffset, chars, start, len); } } this.count = count + delta; return this; } public String substring(int beginIndex) { return substring(beginIndex, count); } public CharSequence subSequence(int beginIndex, int endIndex) { return substring(beginIndex, endIndex); } public synchronized String substring(int beginIndex, int endIndex) { if (((count - endIndex) | beginIndex) < 0 || endIndex < beginIndex) throw new StringIndexOutOfBoundsException(); int len = endIndex - beginIndex; if (len == 0) return ""; byte[] bytes = this.bytes; shared = true; return bytes != null ? new String(beginIndex, bytes, len) : new String(value, beginIndex, len, true); } public synchronized StringBuffer insert(int offset, char[] data, int dataOffset, int count) { int oldCount = this.count; if (data.length - count < dataOffset || ((oldCount - offset) | offset | dataOffset | count) < 0) throw new StringIndexOutOfBoundsException(); ensureCapacityInner(oldCount + count, hasHighByte(data, dataOffset, count)); byte[] bytes = this.bytes; if (bytes != null) { VMSystem.arraycopy(bytes, offset, bytes, offset + count, oldCount - offset); copyCharsToBytes(data, dataOffset, bytes, offset, count); } else { char[] chars = value; VMSystem.arraycopy(chars, offset, chars, offset + count, oldCount - offset); VMSystem.arraycopy(data, dataOffset, chars, offset, count); } this.count = oldCount + count; return this; } public StringBuffer insert(int offset, Object obj) { return insert(offset, obj != null ? obj.toString() : "null"); } public synchronized StringBuffer insert(int offset, String str) { int count = this.count; if (((count - offset) | offset) < 0) throw new StringIndexOutOfBoundsException(offset); if (str == null) str = "null"; int len = str.length(); Object strValue = str.value; byte[] strBytes = null; if (strValue instanceof byte[]) strBytes = (byte[]) strValue; ensureCapacityInner(count + len, len > 0 && strBytes == null); if (len > 0) { int strOffset = str.offset; byte[] bytes = this.bytes; if (bytes != null) { if (offset < count) VMSystem.arraycopy(bytes, offset, bytes, offset + len, count - offset); VMSystem.arraycopy(strBytes, strOffset, bytes, offset, len); } else { char[] chars = value; if (offset < count) VMSystem.arraycopy(chars, offset, chars, offset + len, count - offset); if (strBytes != null) copyBytesToChars(strBytes, strOffset, chars, offset, len); else VMSystem.arraycopy(strValue, strOffset, chars, offset, len); } this.count = count + len; } return this; } public StringBuffer insert(int offset, CharSequence seq) { if (seq == null) seq = "null"; return insert(offset, seq, 0, seq.length()); } public StringBuffer insert(int offset, CharSequence seq, int start, int end) { if (seq == null) seq = "null"; return insert(offset, seq.subSequence(start, end).toString()); } public StringBuffer insert(int offset, char[] data) { return insert(offset, data, 0, data.length); } public StringBuffer insert(int offset, boolean bool) { return insert(offset, bool ? "true" : "false"); } public synchronized StringBuffer insert(int offset, char ch) { int count = this.count; if (((count - offset) | offset) < 0) throw new StringIndexOutOfBoundsException(offset); ensureCapacityInner(count + 1, ch > 0xff); byte[] bytes = this.bytes; if (bytes != null) { VMSystem.arraycopy(bytes, offset, bytes, offset + 1, count - offset); bytes[offset] = (byte) ch; } else { char[] chars = value; VMSystem.arraycopy(chars, offset, chars, offset + 1, count - offset); chars[offset] = ch; } this.count = count + 1; return this; } public StringBuffer insert(int offset, int v) { return insert(offset, String.valueOf(v)); } public StringBuffer insert(int offset, long v) { return insert(offset, Long.toString(v, 10)); } public StringBuffer insert(int offset, float v) { return insert(offset, Float.toString(v)); } public StringBuffer insert(int offset, double v) { return insert(offset, Double.toString(v)); } public int indexOf(String str) { return indexOf(str, 0); } public synchronized int indexOf(String str, int fromIndex) { if (fromIndex <= 0) fromIndex = 0; byte[] bytes = this.bytes; int lastInd = str.length() - 1; int limit = count - lastInd; if (fromIndex < limit) { if (lastInd < 0) return fromIndex; Object strValue = str.value; int strOffset = str.offset; if (bytes != null) { if (strValue instanceof byte[]) { byte[] strBytes = (byte[]) strValue; byte strCh = strBytes[strOffset++]; do { while (bytes[fromIndex] != strCh) if (++fromIndex >= limit) break; if (fromIndex >= limit) break; if (regionMatches(bytes, fromIndex + 1, strBytes, strOffset, lastInd)) return fromIndex; } while (++fromIndex < limit); } else { char[] strChars = (char[]) strValue; char ch = strChars[strOffset++]; if (ch <= 0xff) { byte strCh = (byte)ch; do { while (bytes[fromIndex] != strCh) if (++fromIndex >= limit) break; if (fromIndex >= limit) break; if (regionMatches(bytes, fromIndex + 1, strChars, strOffset, lastInd)) return fromIndex; } while (++fromIndex < limit); } } } else { char[] chars = value; if (strValue instanceof byte[]) { byte[] strBytes = (byte[]) strValue; char strCh = (char)(strBytes[strOffset++] & 0xff); do { while (chars[fromIndex] != strCh) if (++fromIndex >= limit) break; if (fromIndex >= limit) break; if (regionMatches(strBytes, strOffset, chars, fromIndex + 1, lastInd)) return fromIndex; } while (++fromIndex < limit); } else { char[] strChars = (char[]) strValue; char strCh = strChars[strOffset++]; do { while (chars[fromIndex] != strCh) if (++fromIndex >= limit) break; if (fromIndex >= limit) break; if (regionMatches(chars, fromIndex + 1, strChars, strOffset, lastInd)) return fromIndex; } while (++fromIndex < limit); } } } return -1; } public int lastIndexOf(String str) { return lastIndexOf(str, count - str.length()); } public synchronized int lastIndexOf(String str, int fromIndex) { byte[] bytes = this.bytes; int lastInd = str.length() - 1; if (count - lastInd <= fromIndex) fromIndex = count - lastInd - 1; if (fromIndex < 0) return -1; if (lastInd >= 0) { Object strValue = str.value; int strOffset = str.offset; if (bytes != null) { if (strValue instanceof byte[]) { byte[] strBytes = (byte[]) strValue; byte strCh = strBytes[strOffset++]; do { while (bytes[fromIndex] != strCh) if (--fromIndex < 0) break; if (fromIndex < 0) break; } while (!regionMatches(bytes, fromIndex + 1, strBytes, strOffset, lastInd) && --fromIndex >= 0); } else { char[] strChars = (char[]) strValue; char ch = strChars[strOffset++]; if (ch <= 0xff) { byte strCh = (byte)ch; do { while (bytes[fromIndex] != strCh) if (--fromIndex < 0) break; if (fromIndex < 0) break; } while (!regionMatches(bytes, fromIndex + 1, strChars, strOffset, lastInd) && --fromIndex >= 0); } } } else { char[] chars = value; if (strValue instanceof byte[]) { byte[] strBytes = (byte[]) strValue; char strCh = (char)(strBytes[strOffset++] & 0xff); do { while (chars[fromIndex] != strCh) if (--fromIndex < 0) break; if (fromIndex < 0) break; } while (!regionMatches(strBytes, strOffset, chars, fromIndex + 1, lastInd) && --fromIndex >= 0); } else { char[] strChars = (char[]) strValue; char strCh = strChars[strOffset++]; do { while (chars[fromIndex] != strCh) if (--fromIndex < 0) break; if (fromIndex < 0) break; } while (!regionMatches(chars, fromIndex + 1, strChars, strOffset, lastInd) && --fromIndex >= 0); } } } return fromIndex; } public synchronized StringBuffer reverse() { int j = count; ensureCapacityInner(j, false); byte[] bytes = this.bytes; int i = 0; if (bytes != null) { while (--j > i) { byte c = bytes[i]; bytes[i++] = bytes[j]; bytes[j] = c; } } else { char[] chars = value; while (--j > i) { char c = chars[i]; chars[i++] = chars[j]; chars[j] = c; } } return this; } public synchronized void trimToSize() { int count = this.count; if (count >= 0x20) { byte[] bytes = this.bytes; if (bytes != null) { int capacity = bytes.length; int remain = capacity - count; if (remain > 0x200 || (remain >= 0x20 && (capacity >> 2) < remain)) { byte[] newBytes = new byte[count]; VMSystem.arraycopy(bytes, 0, newBytes, 0, count); this.bytes = newBytes; shared = false; } } else { char[] chars = value; int capacity = chars.length; if (hasHighByte(chars, 0, count)) { int remain = capacity - count; if (remain > 0x100 || (remain >= 0x20 && (capacity >> 2) < remain)) { char[] newChars = new char[count]; VMSystem.arraycopy(chars, 0, newChars, 0, count); value = newChars; shared = false; } } else { byte[] newBytes = new byte[count]; copyCharsToBytes(chars, 0, newBytes, 0, count); this.bytes = newBytes; value = null; shared = false; } } } } public synchronized int codePointCount(int beginIndex, int endIndex) { if (((count - endIndex) | beginIndex) < 0 || endIndex < beginIndex) throw new StringIndexOutOfBoundsException(); return bytes != null ? endIndex - beginIndex : Character.codePointCount(value, beginIndex, endIndex - beginIndex); } public synchronized int offsetByCodePoints(int index, int codePointOffset) { int count = this.count; if (((count - index) | index) < 0) throw new StringIndexOutOfBoundsException(index); if (bytes != null) { index = index + codePointOffset; if (((count - index) | index) < 0) throw new IndexOutOfBoundsException(); return index; } return Character.offsetByCodePoints(value, 0, count, index, codePointOffset); } public String toString() /* hard-coded method signature */ { return new String(this); } final Object value() { byte[] bytes = this.bytes; return bytes != null ? (Object) bytes : value; } final void setShared() { shared = true; } private synchronized void writeObject(ObjectOutputStream stream) throws IOException { byte[] bytes = this.bytes; if (bytes != null) { char[] newChars = new char[bytes.length]; copyBytesToChars(bytes, 0, newChars, 0, count); value = newChars; this.bytes = null; } stream.defaultWriteObject(); if (bytes != null) { this.bytes = bytes; value = null; } } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); char[] chars = value; int count = this.count; if (chars == null || count < 0 || chars.length < count) throw new InvalidObjectException("data corrupted"); bytes = null; shared = true; if (!hasHighByte(chars, 0, count)) { byte[] newBytes = new byte[chars.length]; copyCharsToBytes(chars, 0, newBytes, 0, count); bytes = newBytes; value = null; shared = false; } } private void ensureCapacityInner(int minimumCapacity, boolean outOfByte) { byte[] bytes = this.bytes; char[] chars = null; if (minimumCapacity < 0) minimumCapacity = -1 >>> 1; int capacity = bytes != null ? bytes.length : (chars = value).length; if (capacity < minimumCapacity || shared || (outOfByte && bytes != null)) { int newCapacity = capacity; if (capacity < minimumCapacity) { newCapacity = (capacity + 1) << 1; if (newCapacity <= minimumCapacity) newCapacity = minimumCapacity; } int count = this.count; if (bytes != null && !outOfByte) { byte[] newBytes = new byte[newCapacity]; if (count > 0) VMSystem.arraycopy(bytes, 0, newBytes, 0, count); this.bytes = newBytes; } else { char[] newChars = new char[newCapacity]; if (count > 0) { if (bytes != null) { for (int i = 0; i < count; i++) newChars[i] = (char) (bytes[i] & 0xff); } else VMSystem.arraycopy(chars, 0, newChars, 0, count); } value = newChars; this.bytes = null; } shared = false; } } private static boolean regionMatches(byte[] bytes, int offset, byte[] bytes2, int offset2, int count) { while (count-- > 0) if (bytes[offset++] != bytes2[offset2++]) return false; return true; } private static boolean regionMatches(byte[] bytes, int offset, char[] chars2, int offset2, int count) { while (count-- > 0) if ((bytes[offset++] & 0xff) != chars2[offset2++]) return false; return true; } private static boolean regionMatches(char[] chars, int offset, char[] chars2, int offset2, int count) { while (count-- > 0) if (chars[offset++] != chars2[offset2++]) return false; return true; } private static void copyBytesToChars(byte[] bytes, int offset, char[] chars2, int offset2, int count) { while (count-- > 0) chars2[offset2++] = (char) (bytes[offset++] & 0xff); } private static void copyCharsToBytes(char[] chars, int offset, byte[] bytes2, int offset2, int count) { while (count-- > 0) bytes2[offset2++] = (byte) chars[offset++]; } private static boolean hasHighByte(char[] chars, int offset, int count) { count += offset; while (offset < count) if (chars[offset++] > 0xff) return true; return false; } }