diff --git a/src/share/classes/java/lang/AbstractStringBuilder.java b/src/share/classes/java/lang/AbstractStringBuilder.java index 94585f86e50ad417d40ed22e121db196b5565f5a..b87ebd73d9a2dd0329f986d659d2fbb418aad76e 100644 --- a/src/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/share/classes/java/lang/AbstractStringBuilder.java @@ -70,6 +70,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @return the length of the sequence of characters currently * represented by this object */ + @Override public int length() { return count; } @@ -200,6 +201,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@code length()}. */ + @Override public char charAt(int index) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); @@ -431,14 +433,29 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { return this; } + /** + * @since 1.8 + */ + AbstractStringBuilder append(AbstractStringBuilder asb) { + if (asb == null) + return append("null"); + int len = asb.length(); + ensureCapacityInternal(count + len); + asb.getChars(0, len, value, count); + count += len; + return this; + } + // Documentation in subclasses because of synchro difference + @Override public AbstractStringBuilder append(CharSequence s) { if (s == null) s = "null"; if (s instanceof String) return this.append((String)s); - if (s instanceof StringBuffer) - return this.append((StringBuffer)s); + if (s instanceof AbstractStringBuilder) + return this.append((AbstractStringBuilder)s); + return this.append(s, 0, s.length()); } @@ -471,6 +488,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@code start} is greater than {@code end} or * {@code end} is greater than {@code s.length()} */ + @Override public AbstractStringBuilder append(CharSequence s, int start, int end) { if (s == null) s = "null"; @@ -585,6 +603,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @param c a {@code char}. * @return a reference to this object. */ + @Override public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); value[count++] = c; @@ -847,6 +866,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * or if start is greater than end * @spec JSR-51 */ + @Override public CharSequence subSequence(int start, int end) { return substring(start, end); } @@ -1397,6 +1417,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * * @return a string representation of this sequence of characters. */ + @Override public abstract String toString(); /** diff --git a/src/share/classes/java/lang/StringBuffer.java b/src/share/classes/java/lang/StringBuffer.java index 4dee96e679593e2870f4f51da4424e39852417a1..9fabad48d68c895b14bb379413a3b1a718dfbfb5 100644 --- a/src/share/classes/java/lang/StringBuffer.java +++ b/src/share/classes/java/lang/StringBuffer.java @@ -149,15 +149,18 @@ package java.lang; append(seq); } + @Override public synchronized int length() { return count; } + @Override public synchronized int capacity() { return value.length; } + @Override public synchronized void ensureCapacity(int minimumCapacity) { if (minimumCapacity > value.length) { expandCapacity(minimumCapacity); @@ -167,6 +170,7 @@ package java.lang; /** * @since 1.5 */ + @Override public synchronized void trimToSize() { super.trimToSize(); } @@ -175,6 +179,7 @@ package java.lang; * @throws IndexOutOfBoundsException {@inheritDoc} * @see #length() */ + @Override public synchronized void setLength(int newLength) { super.setLength(newLength); } @@ -183,6 +188,7 @@ package java.lang; * @throws IndexOutOfBoundsException {@inheritDoc} * @see #length() */ + @Override public synchronized char charAt(int index) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); @@ -192,6 +198,7 @@ package java.lang; /** * @since 1.5 */ + @Override public synchronized int codePointAt(int index) { return super.codePointAt(index); } @@ -199,6 +206,7 @@ package java.lang; /** * @since 1.5 */ + @Override public synchronized int codePointBefore(int index) { return super.codePointBefore(index); } @@ -206,6 +214,7 @@ package java.lang; /** * @since 1.5 */ + @Override public synchronized int codePointCount(int beginIndex, int endIndex) { return super.codePointCount(beginIndex, endIndex); } @@ -213,6 +222,7 @@ package java.lang; /** * @since 1.5 */ + @Override public synchronized int offsetByCodePoints(int index, int codePointOffset) { return super.offsetByCodePoints(index, codePointOffset); } @@ -221,6 +231,7 @@ package java.lang; * @throws NullPointerException {@inheritDoc} * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public synchronized void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { @@ -231,17 +242,20 @@ package java.lang; * @throws IndexOutOfBoundsException {@inheritDoc} * @see #length() */ + @Override public synchronized void setCharAt(int index, char ch) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); value[index] = ch; } + @Override public synchronized StringBuffer append(Object obj) { super.append(String.valueOf(obj)); return this; } + @Override public synchronized StringBuffer append(String str) { super.append(str); return this; @@ -276,6 +290,14 @@ package java.lang; return this; } + /** + * @since 1.8 + */ + @Override + synchronized StringBuffer append(AbstractStringBuilder asb) { + super.append(asb); + return this; + } /** * Appends the specified {@code CharSequence} to this @@ -298,27 +320,26 @@ package java.lang; * @return a reference to this object. * @since 1.5 */ + @Override public StringBuffer append(CharSequence s) { - // Note, synchronization achieved via other invocations - if (s == null) - s = "null"; - if (s instanceof String) - return this.append((String)s); - if (s instanceof StringBuffer) - return this.append((StringBuffer)s); - return this.append(s, 0, s.length()); + // Note, synchronization achieved via invocations of other StringBuffer methods after + // narrowing of s to specific type + super.append(s); + return this; } /** * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.5 */ + @Override public synchronized StringBuffer append(CharSequence s, int start, int end) { super.append(s, start, end); return this; } + @Override public synchronized StringBuffer append(char[] str) { super.append(str); return this; @@ -327,21 +348,25 @@ package java.lang; /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public synchronized StringBuffer append(char[] str, int offset, int len) { super.append(str, offset, len); return this; } + @Override public synchronized StringBuffer append(boolean b) { super.append(b); return this; } + @Override public synchronized StringBuffer append(char c) { super.append(c); return this; } + @Override public synchronized StringBuffer append(int i) { super.append(i); return this; @@ -350,21 +375,25 @@ package java.lang; /** * @since 1.5 */ + @Override public synchronized StringBuffer appendCodePoint(int codePoint) { super.appendCodePoint(codePoint); return this; } + @Override public synchronized StringBuffer append(long lng) { super.append(lng); return this; } + @Override public synchronized StringBuffer append(float f) { super.append(f); return this; } + @Override public synchronized StringBuffer append(double d) { super.append(d); return this; @@ -374,6 +403,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ + @Override public synchronized StringBuffer delete(int start, int end) { super.delete(start, end); return this; @@ -383,6 +413,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ + @Override public synchronized StringBuffer deleteCharAt(int index) { super.deleteCharAt(index); return this; @@ -392,6 +423,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ + @Override public synchronized StringBuffer replace(int start, int end, String str) { super.replace(start, end, str); return this; @@ -401,6 +433,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ + @Override public synchronized String substring(int start) { return substring(start, count); } @@ -409,6 +442,7 @@ package java.lang; * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.4 */ + @Override public synchronized CharSequence subSequence(int start, int end) { return super.substring(start, end); } @@ -417,6 +451,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ + @Override public synchronized String substring(int start, int end) { return super.substring(start, end); } @@ -425,6 +460,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ + @Override public synchronized StringBuffer insert(int index, char[] str, int offset, int len) { @@ -435,6 +471,7 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public synchronized StringBuffer insert(int offset, Object obj) { super.insert(offset, String.valueOf(obj)); return this; @@ -443,6 +480,7 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public synchronized StringBuffer insert(int offset, String str) { super.insert(offset, str); return this; @@ -451,6 +489,7 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public synchronized StringBuffer insert(int offset, char[] str) { super.insert(offset, str); return this; @@ -460,21 +499,21 @@ package java.lang; * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.5 */ + @Override public StringBuffer insert(int dstOffset, CharSequence s) { - // Note, synchronization achieved via other invocations - if (s == null) - s = "null"; - if (s instanceof String) - return this.insert(dstOffset, (String)s); - return this.insert(dstOffset, s, 0, s.length()); + // Note, synchronization achieved via invocations of other StringBuffer methods + // after narrowing of s to specific type + super.insert(dstOffset, s); + return this; } /** * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.5 */ + @Override public synchronized StringBuffer insert(int dstOffset, CharSequence s, - int start, int end) + int start, int end) { super.insert(dstOffset, s, start, end); return this; @@ -483,13 +522,18 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public StringBuffer insert(int offset, boolean b) { - return insert(offset, String.valueOf(b)); + @Override + public StringBuffer insert(int offset, boolean b) { + // Note, synchronization achieved via invocation of StringBuffer insert(int, String) + // after conversion of b to String by super class method + super.insert(offset, b); + return this; } /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public synchronized StringBuffer insert(int offset, char c) { super.insert(offset, c); return this; @@ -498,54 +542,73 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuffer insert(int offset, int i) { - return insert(offset, String.valueOf(i)); + // Note, synchronization achieved via invocation of StringBuffer insert(int, String) + // after conversion of i to String by super class method + super.insert(offset, i); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuffer insert(int offset, long l) { - return insert(offset, String.valueOf(l)); + // Note, synchronization achieved via invocation of StringBuffer insert(int, String) + // after conversion of l to String by super class method + super.insert(offset, l); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuffer insert(int offset, float f) { - return insert(offset, String.valueOf(f)); + // Note, synchronization achieved via invocation of StringBuffer insert(int, String) + // after conversion of f to String by super class method + super.insert(offset, f); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuffer insert(int offset, double d) { - return insert(offset, String.valueOf(d)); + // Note, synchronization achieved via invocation of StringBuffer insert(int, String) + // after conversion of d to String by super class method + super.insert(offset, d); + return this; } /** * @throws NullPointerException {@inheritDoc} * @since 1.4 */ + @Override public int indexOf(String str) { - return indexOf(str, 0); + // Note, synchronization achieved via invocations of other StringBuffer methods + return super.indexOf(str); } /** * @throws NullPointerException {@inheritDoc} * @since 1.4 */ + @Override public synchronized int indexOf(String str, int fromIndex) { - return String.indexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return super.indexOf(str, fromIndex); } /** * @throws NullPointerException {@inheritDoc} * @since 1.4 */ + @Override public int lastIndexOf(String str) { - // Note, synchronization achieved via other invocations + // Note, synchronization achieved via invocations of other StringBuffer methods return lastIndexOf(str, count); } @@ -553,19 +616,21 @@ package java.lang; * @throws NullPointerException {@inheritDoc} * @since 1.4 */ + @Override public synchronized int lastIndexOf(String str, int fromIndex) { - return String.lastIndexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return super.lastIndexOf(str, fromIndex); } /** * @since JDK1.0.2 */ + @Override public synchronized StringBuffer reverse() { super.reverse(); return this; } + @Override public synchronized String toString() { return new String(value, 0, count); } diff --git a/src/share/classes/java/lang/StringBuilder.java b/src/share/classes/java/lang/StringBuilder.java index eea5f459a90386e72fe73e6f7f86ead8278fdd44..313eae77c3b8b19b3160033f1a622335fca63dfc 100644 --- a/src/share/classes/java/lang/StringBuilder.java +++ b/src/share/classes/java/lang/StringBuilder.java @@ -124,28 +124,17 @@ public final class StringBuilder append(seq); } + @Override public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } + @Override public StringBuilder append(String str) { super.append(str); return this; } - // Appends the specified string builder to this sequence. - private StringBuilder append(StringBuilder sb) { - if (sb == null) - return append("null"); - int len = sb.length(); - int newcount = count + len; - if (newcount > value.length) - expandCapacity(newcount); - sb.getChars(0, len, value, count); - count = newcount; - return this; - } - /** * Appends the specified StringBuffer to this sequence. *

@@ -170,28 +159,22 @@ public final class StringBuilder return this; } - /** - */ + @Override public StringBuilder append(CharSequence s) { - if (s == null) - s = "null"; - if (s instanceof String) - return this.append((String)s); - if (s instanceof StringBuffer) - return this.append((StringBuffer)s); - if (s instanceof StringBuilder) - return this.append((StringBuilder)s); - return this.append(s, 0, s.length()); + super.append(s); + return this; } /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder append(CharSequence s, int start, int end) { super.append(s, start, end); return this; } + @Override public StringBuilder append(char[] str) { super.append(str); return this; @@ -200,36 +183,43 @@ public final class StringBuilder /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder append(char[] str, int offset, int len) { super.append(str, offset, len); return this; } + @Override public StringBuilder append(boolean b) { super.append(b); return this; } + @Override public StringBuilder append(char c) { super.append(c); return this; } + @Override public StringBuilder append(int i) { super.append(i); return this; } + @Override public StringBuilder append(long lng) { super.append(lng); return this; } + @Override public StringBuilder append(float f) { super.append(f); return this; } + @Override public StringBuilder append(double d) { super.append(d); return this; @@ -238,6 +228,7 @@ public final class StringBuilder /** * @since 1.5 */ + @Override public StringBuilder appendCodePoint(int codePoint) { super.appendCodePoint(codePoint); return this; @@ -246,6 +237,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder delete(int start, int end) { super.delete(start, end); return this; @@ -254,6 +246,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder deleteCharAt(int index) { super.deleteCharAt(index); return this; @@ -262,6 +255,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder replace(int start, int end, String str) { super.replace(start, end, str); return this; @@ -270,6 +264,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int index, char[] str, int offset, int len) { @@ -280,13 +275,16 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, Object obj) { - return insert(offset, String.valueOf(obj)); + super.insert(offset, obj); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, String str) { super.insert(offset, str); return this; @@ -295,6 +293,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, char[] str) { super.insert(offset, str); return this; @@ -303,17 +302,16 @@ public final class StringBuilder /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int dstOffset, CharSequence s) { - if (s == null) - s = "null"; - if (s instanceof String) - return this.insert(dstOffset, (String)s); - return this.insert(dstOffset, s, 0, s.length()); + super.insert(dstOffset, s); + return this; } /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int dstOffset, CharSequence s, int start, int end) { @@ -324,6 +322,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, boolean b) { super.insert(offset, b); return this; @@ -332,6 +331,7 @@ public final class StringBuilder /** * @throws IndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, char c) { super.insert(offset, c); return this; @@ -340,66 +340,78 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, int i) { - return insert(offset, String.valueOf(i)); + super.insert(offset, i); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, long l) { - return insert(offset, String.valueOf(l)); + super.insert(offset, l); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, float f) { - return insert(offset, String.valueOf(f)); + super.insert(offset, f); + return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ + @Override public StringBuilder insert(int offset, double d) { - return insert(offset, String.valueOf(d)); + super.insert(offset, d); + return this; } /** * @throws NullPointerException {@inheritDoc} */ + @Override public int indexOf(String str) { - return indexOf(str, 0); + return super.indexOf(str); } /** * @throws NullPointerException {@inheritDoc} */ + @Override public int indexOf(String str, int fromIndex) { - return String.indexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return super.indexOf(str, fromIndex); } /** * @throws NullPointerException {@inheritDoc} */ + @Override public int lastIndexOf(String str) { - return lastIndexOf(str, count); + return super.lastIndexOf(str); } /** * @throws NullPointerException {@inheritDoc} */ + @Override public int lastIndexOf(String str, int fromIndex) { - return String.lastIndexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return super.lastIndexOf(str, fromIndex); } + @Override public StringBuilder reverse() { super.reverse(); return this; } + @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); diff --git a/test/java/lang/StringBuffer/AppendStringBuilder.java b/test/java/lang/StringBuffer/AppendStringBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..a27f6a12a28ac9396e9c2a4e5ef6a76b8e6fec61 --- /dev/null +++ b/test/java/lang/StringBuffer/AppendStringBuilder.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* @test + * @bug 6206780 + * @summary Test StringBuffer.append(StringBuilder); + */ + +import java.util.Random; + +public class AppendStringBuilder { + private static Random generator = new Random(); + + public static void main(String[] args) throws Exception { + for (int i=0; i<1000; i++) { + StringBuilder sb1 = generateTestBuilder(10, 100); + StringBuilder sb2 = generateTestBuilder(10, 100); + StringBuilder sb3 = generateTestBuilder(10, 100); + String s1 = sb1.toString(); + String s2 = sb2.toString(); + String s3 = sb3.toString(); + + String concatResult = new String(s1+s2+s3); + + StringBuffer test = new StringBuffer(); + test.append(sb1); + test.append(sb2); + test.append(sb3); + + if (!test.toString().equals(concatResult)) + throw new RuntimeException("StringBuffer.append failure"); + } + } + + private static int getRandomIndex(int constraint1, int constraint2) { + int range = constraint2 - constraint1; + int x = generator.nextInt(range); + return constraint1 + x; + } + + private static StringBuilder generateTestBuilder(int min, int max) { + StringBuilder aNewStringBuilder = new StringBuilder(120); + int aNewLength = getRandomIndex(min, max); + for(int y=0; y + */ + +import java.util.ArrayList; +import java.util.List; + +public class BufferForwarding { + private final static String A_STRING_BUFFER_VAL = "aStringBuffer"; + private final static String A_STRING_BUILDER_VAL = "aStringBuilder"; + private final static String A_STRING_VAL = "aString"; + private final static String NON_EMPTY_VAL = "NonEmpty"; + + public BufferForwarding() { + System.out.println( "Starting BufferForwarding"); + } + + public static void main(String... args) { + new BufferForwarding().executeTestMethods(); + } + + public void executeTestMethods() { + appendCharSequence(); + indexOfString(); + indexOfStringIntNull(); + indexOfStringNull(); + indexOfStringint(); + insertintCharSequence(); + insertintObject(); + insertintboolean(); + insertintchar(); + insertintdouble(); + insertintfloat(); + insertintint(); + insertintlong(); + lastIndexOfString(); + lastIndexOfStringint(); + } + + public void appendCharSequence() { + // three different flavors of CharSequence + CharSequence aString = A_STRING_VAL; + CharSequence aStringBuilder = new StringBuilder(A_STRING_BUILDER_VAL); + CharSequence aStringBuffer = new StringBuffer(A_STRING_BUFFER_VAL); + + assertEquals( /*actual*/ new StringBuilder().append(aString).toString(), /*expected*/ A_STRING_VAL ); + assertEquals( new StringBuilder().append(aStringBuilder).toString(), A_STRING_BUILDER_VAL ); + assertEquals( new StringBuilder().append(aStringBuffer).toString(), A_STRING_BUFFER_VAL ); + + assertEquals( /*actual*/ new StringBuilder(NON_EMPTY_VAL).append(aString).toString(), NON_EMPTY_VAL+A_STRING_VAL ); + assertEquals( new StringBuilder(NON_EMPTY_VAL).append(aStringBuilder).toString(), NON_EMPTY_VAL+A_STRING_BUILDER_VAL ); + assertEquals( new StringBuilder(NON_EMPTY_VAL).append(aStringBuffer).toString(), NON_EMPTY_VAL+A_STRING_BUFFER_VAL ); + } + + void indexOfString() { + StringBuffer sb = new StringBuffer("xyz"); + assertEquals( sb.indexOf("y"), 1 ); + assertEquals( sb.indexOf("not found"), -1 ); + } + + + public void indexOfStringint() { + StringBuffer sb = new StringBuffer("xyyz"); + assertEquals( sb.indexOf("y",0), 1 ); + assertEquals( sb.indexOf("y",1), 1 ); + assertEquals( sb.indexOf("y",2), 2 ); + assertEquals( sb.indexOf("not found"), -1 ); + } + + + public void indexOfStringIntNull() { + StringBuffer sb = new StringBuffer(); + // should be NPE if null passed + try { + sb.indexOf(null,1); + throw new RuntimeException("Test failed: should have thrown NPE"); + } catch (NullPointerException npe) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown NPE. Instead threw " + + t); + } + } + + + public void indexOfStringNull() { + StringBuffer sb = new StringBuffer(); + + // should be NPE if null passed + try { + sb.indexOf(null); + throw new RuntimeException("Test failed: should have thrown NPE"); + } catch (NullPointerException npe) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown NPE. Instead threw " + + t); + } + } + + + public void insertintboolean() { + boolean b = true; + StringBuffer sb = new StringBuffer("012345"); + assertEquals( sb.insert( 2, b).toString(), "01true2345"); + } + + + public void insertintchar() { + char c = 'C'; + StringBuffer sb = new StringBuffer("012345"); + assertEquals( sb.insert( 2, c ).toString(), "01C2345"); + } + + + public void insertintCharSequence() { + final String initString = "012345"; + // three different flavors of CharSequence + CharSequence aString = A_STRING_VAL; + CharSequence aStringBuilder = new StringBuilder(A_STRING_BUILDER_VAL); + CharSequence aStringBuffer = new StringBuffer(A_STRING_BUFFER_VAL); + + assertEquals( new StringBuffer(initString).insert(2, aString).toString(), "01"+A_STRING_VAL+"2345" ); + + assertEquals( new StringBuffer(initString).insert(2, aStringBuilder).toString(), "01"+A_STRING_BUILDER_VAL+"2345" ); + + assertEquals( new StringBuffer(initString).insert(2, aStringBuffer).toString(), "01"+A_STRING_BUFFER_VAL+"2345" ); + + try { + new StringBuffer(initString).insert(7, aString); + throw new RuntimeException("Test failed: should have thrown IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException soob) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown IndexOutOfBoundsException, but instead threw " + t.getMessage()); + + } + } + + public void insertintdouble() { + double d = 99d; + StringBuffer sb = new StringBuffer("012345"); + assertEquals( sb.insert( 2, d ).toString(), "0199.02345"); } + + + public void insertintfloat() { + float f = 99.0f; + StringBuffer sb = new StringBuffer("012345"); + assertEquals( sb.insert( 2, f ).toString(), "0199.02345"); } + + + public void insertintint() { + int i = 99; + StringBuffer sb = new StringBuffer("012345"); + assertEquals( sb.insert( 2, i ).toString(), "01992345"); + } + + + public void insertintlong() { + long l = 99; + StringBuffer sb = new StringBuffer("012345"); + assertEquals( sb.insert( 2, l ).toString(), "01992345"); } + + public void insertintObject() { + StringBuffer sb = new StringBuffer("012345"); + List ls = new ArrayList(); + ls.add("A"); ls.add("B"); + String lsString = ls.toString(); + assertEquals( sb.insert(2, ls).toString(), "01"+lsString+"2345"); + + try { + sb.insert(sb.length()+1, ls); + throw new RuntimeException("Test failed: should have thrown StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException soob) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown StringIndexOutOfBoundsException, but instead threw:" + + t); + + } + } + + + + public void lastIndexOfString() { + String xyz = "xyz"; + String xyz3 = "xyzxyzxyz"; + StringBuffer sb = new StringBuffer(xyz3); + int pos = sb.lastIndexOf("xyz"); + assertEquals( pos, 2*xyz.length() ); + } + + public void lastIndexOfStringint() { + StringBuffer sb = new StringBuffer("xyzxyzxyz"); + int pos = sb.lastIndexOf("xyz",5); + assertEquals( pos, 3 ); + pos = sb.lastIndexOf("xyz", 6); + assertEquals( pos, 6 ); + } + + public void assertEquals( String actual, String expected) { + if (!actual.equals( expected )) { + throw new RuntimeException( "Test failed: actual = '" + actual + + "', expected = '" + expected + "'"); + } + } + + public void assertEquals( int actual, int expected) { + if (actual != expected) { + throw new RuntimeException( "Test failed: actual = '" + actual + + "', expected = '" + expected + "'"); + } + } +} diff --git a/test/java/lang/StringBuffer/TestSynchronization.java b/test/java/lang/StringBuffer/TestSynchronization.java new file mode 100644 index 0000000000000000000000000000000000000000..75b4b82f3d5d1fb8947af80737cfdc7b65353ad9 --- /dev/null +++ b/test/java/lang/StringBuffer/TestSynchronization.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 6206780 + * @summary Test that all public unsynchronized methods of StringBuffer are either directly or indirectly synchronized + */ +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * TestSynchronization tests whether synchronized methods calls on an object + * result in synchronized calls. Note that this may not test all cases desired. + * It only tests whether some synchronization has occurred on the object during + * the call chain, and can't tell whether the object was locked across all + * operations that have been performed on the object. + */ +public class TestSynchronization { + + /** + * Define parameters used in methods of StringBuffer - admittedly a bit of + * hack but 'purpose-built' for StringBuffer. Something more general could + * probably be developed if the test needs to be more widely adopted. + *

+ * boolean char char[] int double float long Object CharSequence String + * StringBuffer StringBuilder + *

+ */ + private static final boolean BOOLEAN_VAL = true; + private static final char CHAR_VAL = 'x'; + private static final char[] CHAR_ARRAY_VAL = {'c', 'h', 'a', 'r', 'a', 'r', + 'r', 'a', 'y'}; + private static final int INT_VAL = 1; + private static final double DOUBLE_VAL = 1.0d; + private static final float FLOAT_VAL = 1.0f; + private static final long LONG_VAL = 1L; + private static final Object OBJECT_VAL = new Object(); + private static final String STRING_VAL = "String value"; + private static final StringBuilder STRING_BUILDER_VAL = + new StringBuilder("StringBuilder value"); + private static final StringBuffer STRING_BUFFER_VAL = + new StringBuffer("StringBuffer value"); + private static final CharSequence[] CHAR_SEQUENCE_VAL = {STRING_VAL, + STRING_BUILDER_VAL, STRING_BUFFER_VAL}; + + public static void main(String... args) throws Exception { + // First, test the tester + testClass(MyTestClass.class, /* + * self-test + */ true); + // Finally, test StringBuffer + testClass(StringBuffer.class, /* + * self-test + */ false); + } + + /** + * Test all the public, unsynchronized methods of the given class. If + * isSelfTest is true, this is a self-test to ensure that the test program + * itself is working correctly. Should help ensure correctness of this + * program if it changes. + *

+ * @param aClass - the class to test + * @param isSelfTest - true if this is the special self-test class + * @throws SecurityException + */ + private static void testClass(Class aClass, boolean isSelfTest) throws + Exception { + // Get all unsynchronized public methods via reflection. We don't need + // to test synchronized methods. By definition. they are already doing + // the right thing. + List methods = Arrays.asList(aClass.getDeclaredMethods()); + for (Method m : methods) { + int modifiers = m.getModifiers(); + if (Modifier.isPublic(modifiers) + && !Modifier.isSynchronized(modifiers)) { + try { + testMethod(aClass, m); + } catch (TestFailedException e) { + if (isSelfTest) { + String methodName = e.getMethod().getName(); + switch (methodName) { + case "should_pass": + throw new RuntimeException( + "Test failed: self-test failed. The 'should_pass' method did not pass the synchronization test. Check the test code."); + case "should_fail": + break; + default: + throw new RuntimeException( + "Test failed: something is amiss with the test. A TestFailedException was generated on a call to " + + methodName + " which we didn't expect to test in the first place."); + } + } else { + throw new RuntimeException("Test failed: the method " + + e.getMethod().toString() + + " should be synchronized, but isn't."); + } + } + } + } + } + + private static void invokeMethod(Class aClass, final Method m, + final Object[] args) throws TestFailedException, Exception { + //System.out.println( "Invoking " + m.toString() + " with parameters " + Arrays.toString(args)); + final Constructor objConstructor; + Object obj = null; + + objConstructor = aClass.getConstructor(String.class); + obj = objConstructor.newInstance("LeftPalindrome-emordnilaP-thgiR"); + + // test method m for synchronization + if (!isSynchronized(m, obj, args)) { + throw new TestFailedException(m); + } + } + + private static void testMethod(Class aClass, Method m) throws + Exception { + /* + * Construct call with arguments of the correct type. Note that the + * values are somewhat irrelevant. If the call actually succeeds, it + * means we aren't synchronized and the test has failed. + */ + Class[] pTypes = m.getParameterTypes(); + List charSequenceArgs = new ArrayList<>(); + Object[] args = new Object[pTypes.length]; + for (int i = 0; i < pTypes.length; i++) { + // determine the type and create the corresponding actual argument + Class pType = pTypes[i]; + if (pType.equals(boolean.class)) { + args[i] = BOOLEAN_VAL; + } else if (pType.equals(char.class)) { + args[i] = CHAR_VAL; + } else if (pType.equals(int.class)) { + args[i] = INT_VAL; + } else if (pType.equals(double.class)) { + args[i] = DOUBLE_VAL; + } else if (pType.equals(float.class)) { + args[i] = FLOAT_VAL; + } else if (pType.equals(long.class)) { + args[i] = LONG_VAL; + } else if (pType.equals(Object.class)) { + args[i] = OBJECT_VAL; + } else if (pType.equals(StringBuilder.class)) { + args[i] = STRING_BUILDER_VAL; + } else if (pType.equals(StringBuffer.class)) { + args[i] = STRING_BUFFER_VAL; + } else if (pType.equals(String.class)) { + args[i] = STRING_VAL; + } else if (pType.isArray() && pType.getComponentType().equals(char.class)) { + args[i] = CHAR_ARRAY_VAL; + } else if (pType.equals(CharSequence.class)) { + charSequenceArgs.add(new Integer(i)); + } else { + throw new RuntimeException("Test Failed: not accounting for method call with parameter type of " + pType.getName() + " You must update the test."); + } + } + /* + * If there are no CharSequence args, we can simply invoke our method + * and test it + */ + if (charSequenceArgs.isEmpty()) { + invokeMethod(aClass, m, args); + } else { + /* + * Iterate through the different CharSequence types and invoke the + * method for each type. + */ + if (charSequenceArgs.size() > 1) { + throw new RuntimeException("Test Failed: the test cannot handle a method with multiple CharSequence arguments. You must update the test to handle the method " + + m.toString()); + } + for (int j = 0; j < CHAR_SEQUENCE_VAL.length; j++) { + args[charSequenceArgs.get(0)] = CHAR_SEQUENCE_VAL[j]; + invokeMethod(aClass, m, args); + } + } + } + + @SuppressWarnings("serial") + private static class TestFailedException extends Exception { + + final Method m; + + public Method getMethod() { + return m; + } + + public TestFailedException(Method m) { + this.m = m; + } + } + + static class InvokeTask implements Runnable { + + private final Method m; + private final Object target; + private final Object[] args; + + InvokeTask(Method m, Object target, Object... args) { + this.m = m; + this.target = target; + this.args = args; + } + + @Override + public void run() { + try { + m.invoke(target, args); + } catch (IllegalAccessException | IllegalArgumentException | + InvocationTargetException e) { + e.printStackTrace(); + } + } + } + + /** + * isSynchronized tests whether the given method is synchronized or not by + * invoking it in a thread and testing the thread state after starting the + * thread + *

+ * @param m the method to test + * @param target the object the method is executed on + * @param args the arguments passed to the method + * @return true iff the method is synchronized + */ + private static boolean isSynchronized(Method m, Object target, + Object... args) { + Thread t = new Thread(new InvokeTask(m, target, args)); + + Boolean isSynchronized = null; + + synchronized (target) { + t.start(); + + while (isSynchronized == null) { + switch (t.getState()) { + case NEW: + case RUNNABLE: + case WAITING: + case TIMED_WAITING: + Thread.yield(); + break; + case BLOCKED: + isSynchronized = true; + break; + case TERMINATED: + isSynchronized = false; + break; + } + } + } + + try { + t.join(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + + return isSynchronized; + } + + /* + * This class is used to test the synchronization tester above. It has a + * method, should_pass, that is unsynchronized but calls a synchronized + * method. It has another method, should_fail, which isn't synchronized and + * doesn't call a synchronized method. The former should pass and the latter + * should fail. + */ + private static class MyTestClass { + + @SuppressWarnings("unused") + public MyTestClass(String s) { + } + + @SuppressWarnings("unused") + public void should_pass() { + // call sync method + sync_shouldnt_be_tested(); + } + + @SuppressWarnings("unused") + public void should_fail() { + } + + public synchronized void sync_shouldnt_be_tested() { + } + } +} diff --git a/test/java/lang/StringBuilder/AppendStringBuffer.java b/test/java/lang/StringBuilder/AppendStringBuffer.java new file mode 100644 index 0000000000000000000000000000000000000000..a54cf0a0d29bb3293e2c706096ad1a683d1003d8 --- /dev/null +++ b/test/java/lang/StringBuilder/AppendStringBuffer.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 6206780 + * @summary Test StringBuilder.append(StringBuffer); + */ + +import java.util.Random; + +public class AppendStringBuffer { + private static Random generator = new Random(); + + public static void main(String[] args) throws Exception { + for (int i=0; i<1000; i++) { + StringBuffer sb1 = generateTestBuffer(10, 100); + StringBuffer sb2 = generateTestBuffer(10, 100); + StringBuffer sb3 = generateTestBuffer(10, 100); + String s1 = sb1.toString(); + String s2 = sb2.toString(); + String s3 = sb3.toString(); + + String concatResult = new String(s1+s2+s3); + + StringBuilder test = new StringBuilder(); + test.append(sb1); + test.append(sb2); + test.append(sb3); + + if (!test.toString().equals(concatResult)) + throw new RuntimeException("StringBuffer.append failure"); + } + } + + private static int getRandomIndex(int constraint1, int constraint2) { + int range = constraint2 - constraint1; + int x = generator.nextInt(range); + return constraint1 + x; + } + + private static StringBuffer generateTestBuffer(int min, int max) { + StringBuffer aNewStringBuffer = new StringBuffer(120); + int aNewLength = getRandomIndex(min, max); + for(int y=0; y + */ + +import java.util.ArrayList; +import java.util.List; + +public class BuilderForwarding { + private final static String A_STRING_BUFFER_VAL = "aStringBuffer"; + private final static String A_STRING_BUILDER_VAL = "aStringBuilder"; + private final static String A_STRING_VAL = "aString"; + private final static String NON_EMPTY_VAL = "NonEmpty"; + + public BuilderForwarding() { + System.out.println( "Starting BuilderForwarding"); + } + + public static void main(String... args) { + new BuilderForwarding().executeTestMethods(); + } + + public void executeTestMethods() { + appendCharSequence(); + indexOfString(); + indexOfStringIntNull(); + indexOfStringNull(); + indexOfStringint(); + insertintCharSequence(); + insertintObject(); + insertintboolean(); + insertintchar(); + insertintdouble(); + insertintfloat(); + insertintint(); + insertintlong(); + lastIndexOfString(); + lastIndexOfStringint(); + } + + public void appendCharSequence() { + // three different flavors of CharSequence + CharSequence aString = A_STRING_VAL; + CharSequence aStringBuilder = new StringBuilder(A_STRING_BUILDER_VAL); + CharSequence aStringBuffer = new StringBuffer(A_STRING_BUFFER_VAL); + + assertEquals( /*actual*/ new StringBuilder().append(aString).toString(), /*expected*/ A_STRING_VAL ); + assertEquals( new StringBuilder().append(aStringBuilder).toString(), A_STRING_BUILDER_VAL ); + assertEquals( new StringBuilder().append(aStringBuffer).toString(), A_STRING_BUFFER_VAL ); + + assertEquals( /*actual*/ new StringBuilder(NON_EMPTY_VAL).append(aString).toString(), NON_EMPTY_VAL+A_STRING_VAL ); + assertEquals( new StringBuilder(NON_EMPTY_VAL).append(aStringBuilder).toString(), NON_EMPTY_VAL+A_STRING_BUILDER_VAL ); + assertEquals( new StringBuilder(NON_EMPTY_VAL).append(aStringBuffer).toString(), NON_EMPTY_VAL+A_STRING_BUFFER_VAL ); + } + + + public void indexOfString() { + StringBuilder sb = new StringBuilder(); + // should be NPE if null passed + try { + + sb.indexOf(null); + throw new RuntimeException("Test failed: should have thrown NPE"); + + + } catch (NullPointerException npe) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown NPE. Instead threw " + + t); + } + sb = new StringBuilder("xyz"); + assertEquals( sb.indexOf("y"), 1 ); + assertEquals( sb.indexOf("not found"), -1 ); + } + + + public void indexOfStringint() { + StringBuilder sb = new StringBuilder(); + // should be NPE if null passed + try { + sb.indexOf(null,1); + throw new RuntimeException("Test failed: should have thrown NPE"); + } catch (NullPointerException npe) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown NPE"); + } + sb = new StringBuilder("xyyz"); + assertEquals( sb.indexOf("y",0), 1 ); + assertEquals( sb.indexOf("y",1), 1 ); + assertEquals( sb.indexOf("y",2), 2 ); + assertEquals( sb.indexOf("not found"), -1 ); + } + + + public void indexOfStringIntNull() { + StringBuffer sb = new StringBuffer(); + // should be NPE if null passed + try { + sb.indexOf(null,1); + throw new RuntimeException("Test failed: should have thrown NPE"); + } catch (NullPointerException npe) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown NPE. Instead threw " + + t); + } + } + + + public void indexOfStringNull() { + StringBuilder sb = new StringBuilder(); + + // should be NPE if null passed + try { + sb.indexOf(null); + throw new RuntimeException("Test failed: should have thrown NPE"); + } catch (NullPointerException npe) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown NPE. Instead threw " + + t); + } + } + + + public void insertintboolean() { + boolean b = true; + StringBuilder sb = new StringBuilder("012345"); + assertEquals( sb.insert( 2, b).toString(), "01true2345"); + } + + + public void insertintchar() { + char c = 'C'; + StringBuilder sb = new StringBuilder("012345"); + assertEquals( sb.insert( 2, c ).toString(), "01C2345"); + } + + + public void insertintCharSequence() { + final String initString = "012345"; + // three different flavors of CharSequence + CharSequence aString = A_STRING_VAL; + CharSequence aStringBuilder = new StringBuilder(A_STRING_BUILDER_VAL); + CharSequence aStringBuffer = new StringBuffer(A_STRING_BUFFER_VAL); + + assertEquals( new StringBuilder(initString).insert(2, aString).toString(), "01"+A_STRING_VAL+"2345" ); + + assertEquals( new StringBuilder(initString).insert(2, aStringBuilder).toString(), "01"+A_STRING_BUILDER_VAL+"2345" ); + + assertEquals( new StringBuilder(initString).insert(2, aStringBuffer).toString(), "01"+A_STRING_BUFFER_VAL+"2345" ); + + try { + new StringBuilder(initString).insert(7, aString); + throw new RuntimeException("Test failed: should have thrown IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException soob) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown IndexOutOfBoundsException, but instead threw " + t.getMessage()); + + } + } + + + public void insertintdouble() { + double d = 99d; + StringBuilder sb = new StringBuilder("012345"); + assertEquals( sb.insert( 2, d ).toString(), "0199.02345"); } + + + public void insertintfloat() { + float f = 99.0f; + StringBuilder sb = new StringBuilder("012345"); + assertEquals( sb.insert( 2, f ).toString(), "0199.02345"); } + + + public void insertintint() { + int i = 99; + StringBuilder sb = new StringBuilder("012345"); + assertEquals( sb.insert( 2, i ).toString(), "01992345"); + } + + + public void insertintlong() { + long l = 99; + StringBuilder sb = new StringBuilder("012345"); + assertEquals( sb.insert( 2, l ).toString(), "01992345"); } + + + public void insertintObject() { + StringBuilder sb = new StringBuilder("012345"); + List ls = new ArrayList(); + ls.add("A"); ls.add("B"); + String lsString = ls.toString(); + assertEquals( sb.insert(2, ls).toString(), "01"+lsString+"2345"); + + try { + sb.insert(sb.length()+1, ls); + throw new RuntimeException("Test failed: should have thrown StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException soob) { + // expected: passed + } catch (Throwable t) { + throw new RuntimeException("Test failed: should have thrown StringIndexOutOfBoundsException, but instead threw:" + + t); + } + } + + + public void lastIndexOfString() { + String xyz = "xyz"; + String xyz3 = "xyzxyzxyz"; + StringBuilder sb = new StringBuilder(xyz3); + int pos = sb.lastIndexOf("xyz"); + assertEquals( pos, 2*xyz.length() ); + } + + + public void lastIndexOfStringint() { + StringBuilder sb = new StringBuilder("xyzxyzxyz"); + int pos = sb.lastIndexOf("xyz",5); + assertEquals( pos, 3 ); + pos = sb.lastIndexOf("xyz", 6); + assertEquals( pos, 6 ); + } + + public void assertEquals( String actual, String expected) { + if (!actual.equals( expected )) { + throw new RuntimeException( "Test failed: actual = '" + actual + + "', expected = '" + expected + "'"); + } + } + + public void assertEquals( int actual, int expected) { + if (actual != expected) { + throw new RuntimeException( "Test failed: actual = '" + actual + + "', expected = '" + expected + "'"); + } + } +} +