6206780: (str) Forwarding append methods in String{Buffer,Builder} are inconsistent
Summary: update StringBuilder & StringBuffer to consistently handle forwarding to AbstractStringBuilder. Some additional cleanup (removal of refs to sub-classes from AbstractStringBuilder)
Reviewed-by: chegar, alanb, mduigou
diff --git a/test/java/lang/StringBuffer/AppendStringBuilder.java b/test/java/lang/StringBuffer/AppendStringBuilder.java
new file mode 100644
index 0000000..a27f6a1
--- /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<aNewLength; y++) {
+ int achar = generator.nextInt(30)+30;
+ char test = (char)(achar);
+ aNewStringBuilder.append(test);
+ }
+ return aNewStringBuilder;
+ }
+}
diff --git a/test/java/lang/StringBuffer/BufferForwarding.java b/test/java/lang/StringBuffer/BufferForwarding.java
new file mode 100644
index 0000000..fafbc19
--- /dev/null
+++ b/test/java/lang/StringBuffer/BufferForwarding.java
@@ -0,0 +1,242 @@
+/*
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 forwarding of methods to super in StringBuffer
+ * @author Jim Gish <jim.gish@oracle.com>
+ */
+
+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<String> ls = new ArrayList<String>();
+ 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 0000000..75b4b82
--- /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.
+ * <p/>
+ * boolean char char[] int double float long Object CharSequence String
+ * StringBuffer StringBuilder
+ * <p/>
+ */
+ 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.
+ * <p/>
+ * @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<Method> 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<Integer> 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
+ * <p/>
+ * @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 0000000..a54cf0a
--- /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<aNewLength; y++) {
+ int achar = generator.nextInt(30)+30;
+ char test = (char)(achar);
+ aNewStringBuffer.append(test);
+ }
+ return aNewStringBuffer;
+ }
+}
diff --git a/test/java/lang/StringBuilder/BuilderForwarding.java b/test/java/lang/StringBuilder/BuilderForwarding.java
new file mode 100644
index 0000000..99334ff
--- /dev/null
+++ b/test/java/lang/StringBuilder/BuilderForwarding.java
@@ -0,0 +1,269 @@
+/*
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 forwarding of methods to super in StringBuilder
+ * @author Jim Gish <jim.gish@oracle.com>
+ */
+
+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<String> ls = new ArrayList<String>();
+ 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 + "'");
+ }
+ }
+}
+