/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 1998, 1999 Wabasoft <www.wabasoft.com> * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine 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. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.lang; /** * This class is a growable buffer for characters. It is mainly used * to create Strings. The compiler uses it to implement the "+" operator. * For example: * <pre> * "a" + 4 + "c" * </pre> * is compiled to: * <pre> * new StringBuffer().append("a").append(4).append("c").toString() * </pre> * ... to concatenate multiple strings together. In the code shown, the * compiler generates references to the StringBuffer class to append the * objects together. * <p> * If you use it in loop, consider creating a new StringBuffer with an * initial number of characters, and calling setLength before each use. * This can improve the performance up to 10x. * Example: * <pre> * StringBuffer sb = new StringBuffer(1024); * for (...) * { * sb.setLength(0); * sb.append(...).append(...).append(...); * String s = sb.toString(); * } * </pre> * IMPORTANT: the totalcross.lang package is the java.lang that will be used in the device. * You CANNOT use nor import totalcross.lang package in desktop. When tc.Deploy is called, * all references to java.lang are replaced by totalcross.lang automatically. Given this, * you must use only the classes and methods that exists BOTH in java.lang and totalcross.lang. * For example, you can't use java.lang.ClassLoader because there are no totalcross.lang.ClassLoader. * Another example, you can't use java.lang.String.indexOfIgnoreCase because there are no * totalcross.lang.String.indexOfIgnoreCase method. Trying to use a class or method from the java.lang package * that has no correspondence with totalcross.lang will make the tc.Deploy program to abort, informing * where the problem occured. A good idea is to always refer to this javadoc to know what is and what isn't * available. */ public final class StringBuffer4D { /** The buffer is used for character storage. */ char charbuf[]; /** The count is the number of characters in the buffer. */ int count; public static final int STARTING_SIZE = 16; // read by the StringBuffer test cases. /** * Constructs an empty String buffer. * Consider using the other constructor where you pass an initial size. */ public StringBuffer4D() { charbuf = new char[STARTING_SIZE]; } /** * Constructs an empty String buffer with the specified initial length. * @param length the initial length */ public StringBuffer4D(int length) { charbuf = new char[length]; } /** * Constructs a String buffer with the specified initial buffer. * @param str the initial buffer of the buffer */ public StringBuffer4D(String4D str) { int len = count = str.chars.length; // nopt charbuf = new char[len + 10]; // guich@320_8 String4D.copyChars(str.chars, 0, charbuf, 0, len); } /** * Returns the number of characters in the buffer. */ public int length() { return count; } /** Replaces the char at the given index. * @since SuperWaba 4.02 */ public void setCharAt(int index, char ch) // guich@402_20 { charbuf[index] = ch; } /** * Returns the current capacity of the String buffer. The capacity * is the amount of storage available for newly inserted * characters; beyond which a buffer reallocation will occur. */ public int capacity() { return charbuf.length; } /** * Ensures that the capacity of the buffer is at least equal to the * specified minimum. * @param minimumCapacity the minimum desired capacity in characters */ native public void ensureCapacity(int minimumCapacity); /** * Sets the length of the String. If the length is reduced, characters * are lost. If the length is extended, the values of the new characters * are set to 0. * @param newLength the new length of the buffer */ native public void setLength(int newLength); /** * Returns the character at the specified index. An index ranges * from 0..length()-1. * @param index the index of the desired character */ public char charAt(int index) { return (index >= 0 && index < count) ? charbuf[index] : '\0'; } /** * Copies the characters of the specified substring (determined by * srcBegin and srcEnd) into the character array, starting at the * array's dstBegin location. Both srcBegin and srcEnd must be legal * indexes into the buffer. * @param srcBegin begin copy at this offset in the String * @param srcEnd stop copying at this offset in the String * @param dst the array to copy the data into * @param dstBegin offset into dst */ public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { String4D.copyChars(charbuf, srcBegin, dst, dstBegin, srcEnd - srcBegin); } /** * Appends an object to the end of this buffer. * @param obj the object to be appended * @return the StringBuffer itself, NOT a new one. */ public StringBuffer4D append(Object obj) // can't be native bc obj.toString is implemented in Java { return append(obj != null ? obj.toString() : "null"); } /** * Appends a String to the end of this buffer. * @param str the String to be appended * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(String str); /** * Appends an array of characters to the end of this buffer. * @param str the characters to be appended * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(char str[]); /** * Appends a range of an array of characters to the end of this buffer. * @param str the characters to be appended * @param offset where to start * @param len the number of characters to add * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(char str[], int offset, int len); /** * Appends a boolean to the end of this buffer. * @param b the boolean to be appended * @return the StringBuffer itself, NOT a new one. */ public StringBuffer4D append(boolean b) { return append(b?"true":"false"); } /** * Appends a character to the end of this buffer. * @param c the character to be appended * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(char c); /** * Appends an integer to the end of this buffer. * @param i the integer to be appended * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(int i); /** * Appends a long to the end of this buffer. * @param l the long to be appended * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(long l); /** * Appends a double to the end of this buffer. * @param d the double to be appended * @return the StringBuffer itself, NOT a new one. */ native public StringBuffer4D append(double d); /** * Converts to a String representing the data in the buffer. */ public String toString() { return new String(charbuf, 0, count); } /** * Removes the characters in a substring of this <code>StringBuffer</code>. * The substring begins at the specified <code>start</code> and extends to * the character at index <code>end - 1</code> or to the end of the * <code>StringBuffer</code> if no such character exists. If * <code>start</code> is equal to <code>end</code>, no changes are made. * If any paramter goes beyond limits, it is enforced into limits. * If start > end, the string is emptied; * Returns <code>this</code> StringBuffer. */ native public StringBuffer4D delete4D(int start, int end); // guich@330_30 /** Reverses the string. * @since SuperWaba 4.5 */ public StringBuffer4D reverse() { int i = count - 1; char []buf = charbuf; for(int j = (i - 1) >> 1; j >= 0; j--) { char c = buf[j]; buf[j] = buf[i - j]; buf[i - j] = c; } return this; } /** * Append the <code>StringBuffer4D</code> value of the argument to this * <code>StringBuffer4D</code>. This behaves the same as * <code>append((Object) stringBuffer)</code>, except it is more efficient. * * @param stringBuffer the <code>StringBuffer4D</code> to convert and append * @return this <code>StringBuffer4D</code> * @see #append(Object) */ public StringBuffer4D append(StringBuffer4D stringBuffer) { return append(stringBuffer.charbuf,0,count); } /** * Delete a character from this <code>StringBuffer4D</code>. * * @param index the index of the character to delete * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if index is out of bounds */ public StringBuffer4D deleteCharAt(int index) { return delete4D(index, index + 1); } /** * Replace characters between index <code>start</code> (inclusive) and * <code>end</code> (exclusive) with <code>str</code>. If <code>end</code> * is larger than the size of this StringBuffer4D, all characters after * <code>start</code> are replaced. * * @param start the beginning index of characters to delete (inclusive) * @param end the ending index of characters to delete (exclusive) * @param str the new <code>String</code> to insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if start or end are out of bounds * @throws NullPointerException if str is null */ public StringBuffer4D replace(int start, int end, String4D str) { if (start < 0 || start > count || start > end) throw new StringIndexOutOfBoundsException(start); int len = str.chars.length; // Calculate the difference in 'count' after the replace. int delta = len - (end > count ? count : end) + start; ensureCapacity(count + delta); if (delta != 0 && end < count) String4D.copyChars(charbuf, end, charbuf, end + delta, count - end); str.getChars(0, len, charbuf, start); count += delta; return this; } /** * Creates a substring of this StringBuffer4D, starting at a specified index * and ending at the end of this StringBuffer4D. * * @param beginIndex index to start substring (base 0) * @return new String which is a substring of this StringBuffer4D * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds * @see #substring(int, int) */ public String4D substring(int beginIndex) { return substring(beginIndex, count); } /** * Creates a substring of this StringBuffer4D, starting at a specified index * and ending at one character before a specified index. * * @param beginIndex index to start at (inclusive, base 0) * @param endIndex index to end at (exclusive) * @return new String which is a substring of this StringBuffer4D * @throws StringIndexOutOfBoundsException if beginIndex or endIndex is out * of bounds */ public String4D substring(int beginIndex, int endIndex) { int len = endIndex - beginIndex; if (beginIndex < 0 || endIndex > count || endIndex < beginIndex) throw new StringIndexOutOfBoundsException(); if (len == 0) return new String4D(); return new String4D(charbuf, beginIndex, len); } /** * Insert a subarray of the <code>char[]</code> argument into this * <code>StringBuffer4D</code>. * * @param offset the place to insert in this buffer * @param str the <code>char[]</code> to insert * @param str_offset the index in <code>str</code> to start inserting from * @param len the number of characters to insert * @return this <code>StringBuffer4D</code> * @throws NullPointerException if <code>str</code> is <code>null</code> * @throws StringIndexOutOfBoundsException if any index is out of bounds */ public StringBuffer4D insert(int offset, char[] str, int str_offset, int len) { if (offset < 0 || offset > count || len < 0 || str_offset < 0 || str_offset > str.length - len) throw new StringIndexOutOfBoundsException(); ensureCapacity(count + len); String4D.copyChars(charbuf, offset, charbuf, offset + len, count - offset); String4D.copyChars(str, str_offset, charbuf, offset, len); count += len; return this; } /** * Insert the <code>String</code> value of the argument into this * <code>StringBuffer4D</code>. Uses <code>String.valueOf()</code> to convert * to <code>String</code>. * * @param offset the place to insert in this buffer * @param obj the <code>Object</code> to convert and insert * @return this <code>StringBuffer4D</code> * @exception StringIndexOutOfBoundsException if offset is out of bounds * @see String#valueOf(Object) */ public StringBuffer4D insert(int offset, Object obj) { return insert(offset, obj == null ? "null" : obj.toString()); } /** * Insert the <code>String</code> argument into this * <code>StringBuffer4D</code>. If str is null, the String "null" is used * instead. * * @param offset the place to insert in this buffer * @param str the <code>String</code> to insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds */ public StringBuffer4D insert(int offset, String4D str) { if (offset < 0 || offset > count) throw new StringIndexOutOfBoundsException(offset); if (str == null) str = new String4D(new char[]{'n','u','l','l'}); int len = str.chars.length; ensureCapacity(count + len); String4D.copyChars(charbuf, offset, charbuf, offset + len, count - offset); str.getChars(0, len, charbuf, offset); count += len; return this; } /** * Insert the <code>char[]</code> argument into this * <code>StringBuffer4D</code>. * * @param offset the place to insert in this buffer * @param data the <code>char[]</code> to insert * @return this <code>StringBuffer4D</code> * @throws NullPointerException if <code>data</code> is <code>null</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds * @see #insert(int, char[], int, int) */ public StringBuffer4D insert(int offset, char[] data) { return insert(offset, data, 0, data.length); } /** * Insert the <code>String</code> value of the argument into this * <code>StringBuffer4D</code>. Uses <code>String.valueOf()</code> to convert * to <code>String</code>. * * @param offset the place to insert in this buffer * @param bool the <code>boolean</code> to convert and insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds * @see String#valueOf(boolean) */ public StringBuffer4D insert(int offset, boolean bool) { return insert(offset, bool ? "true" : "false"); } /** * Insert the <code>char</code> argument into this <code>StringBuffer4D</code>. * * @param offset the place to insert in this buffer * @param ch the <code>char</code> to insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds */ public StringBuffer4D insert(int offset, char ch) { if (offset < 0 || offset > count) throw new StringIndexOutOfBoundsException(offset); ensureCapacity(count + 1); String4D.copyChars(charbuf, offset, charbuf, offset + 1, count - offset); charbuf[offset] = ch; count++; return this; } /** * Insert the <code>String</code> value of the argument into this * <code>StringBuffer4D</code>. Uses <code>String.valueOf()</code> to convert * to <code>String</code>. * * @param offset the place to insert in this buffer * @param inum the <code>int</code> to convert and insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds * @see String#valueOf(int) */ public StringBuffer4D insert(int offset, int inum) { return insert(offset, String.valueOf(inum)); } /** * Insert the <code>String</code> value of the argument into this * <code>StringBuffer4D</code>. Uses <code>String.valueOf()</code> to convert * to <code>String</code>. * * @param offset the place to insert in this buffer * @param lnum the <code>long</code> to convert and insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds * @see String#valueOf(long) */ public StringBuffer4D insert(int offset, long lnum) { return insert(offset, String.valueOf(lnum)); } /** * Insert the <code>String</code> value of the argument into this * <code>StringBuffer4D</code>. Uses <code>String.valueOf()</code> to convert * to <code>String</code>. * * @param offset the place to insert in this buffer * @param dnum the <code>double</code> to convert and insert * @return this <code>StringBuffer4D</code> * @throws StringIndexOutOfBoundsException if offset is out of bounds * @see String#valueOf(double) */ public StringBuffer4D insert(int offset, double dnum) { return insert(offset, String.valueOf(dnum)); } /** * Finds the first instance of a substring in this StringBuilder. * * @param str String to find * @return location (base 0) of the String, or -1 if not found * @throws NullPointerException if str is null */ public int indexOf(String4D str) { return indexOf(str, 0); } /** * Finds the first instance of a String in this StringBuffer, starting at * a given index. If starting index is less than 0, the search starts at * the beginning of this String. If the starting index is greater than the * length of this String, or the substring is not found, -1 is returned. * * @param str String to find * @param fromIndex index to start the search * @return location (base 0) of the String, or -1 if not found * @throws NullPointerException if str is null * @since 1.4 */ public int indexOf(String4D str, int fromIndex) { if (fromIndex < 0) fromIndex = 0; int limit = count - str.chars.length; for ( ; fromIndex <= limit; fromIndex++) if (regionMatches(fromIndex, str)) return fromIndex; return -1; } /** * Finds the last instance of a substring in this StringBuffer. * * @param str String to find * @return location (base 0) of the String, or -1 if not found * @throws NullPointerException if str is null */ public int lastIndexOf(String4D str) { return lastIndexOf(str, count - str.chars.length); } /** * Finds the last instance of a String in this StringBuffer, starting at a * given index. If starting index is greater than the maximum valid index, * then the search begins at the end of this String. If the starting index * is less than zero, or the substring is not found, -1 is returned. * * @param str String to find * @param fromIndex index to start the search * @return location (base 0) of the String, or -1 if not found * @throws NullPointerException if str is null */ public int lastIndexOf(String4D str, int fromIndex) { fromIndex = Math.min(fromIndex, count - str.chars.length); for ( ; fromIndex >= 0; fromIndex--) if (regionMatches(fromIndex, str)) return fromIndex; return -1; } private boolean regionMatches(int toffset, String4D other) { int len = other.chars.length; int index = 0; while (--len >= 0) if (charbuf[toffset++] != other.chars[index++]) return false; return true; } }