add StringBuffer/Builder.reverse

fixes ISSUE 449

Change-Id: I342e7ed86c0e4efa90d09bdfe0d72c79db2654c5
Review-Link: https://gwt-review.googlesource.com/#/c/2431/

Review by: goktug@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11591 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java b/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java
index 23a7202..86c9b52 100644
--- a/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.core.client.impl;
 
+
 /**
  * <p>
  * The interface to defer bound implementations of {@link StringBuilder} and
@@ -29,9 +30,35 @@
  */
 public abstract class StringBufferImpl {
 
+  public static String reverseString(String s) {
+    int length = s.length();
+
+    if (length <= 1) {
+      return s;
+    }
+
+    char[] buffer = new char[length];
+
+    buffer[0] = s.charAt(length - 1);
+
+    for (int i = 1; i < length; i++) {
+      buffer[i] = s.charAt(length - 1 - i);
+      if (Character.isSurrogatePair(buffer[i], buffer[i - 1])) {
+        swap(buffer, i - 1, i);
+      }
+    }
+
+    return new String(buffer);
+  }
+
+  private static void swap(char[] buffer, int f, int s) {
+    char tmp = buffer[f];
+    buffer[f] = buffer[s];
+    buffer[s] = tmp;
+  }
+
   /**
-   * Append for primitive; the value can be stored and only later converted to a
-   * string.
+   * Append for primitive; the value can be stored and only later converted to a string.
    */
   public abstract void append(Object data, boolean x);
 
@@ -86,6 +113,11 @@
   public abstract void replace(Object data, int start, int end, String toInsert);
 
   /**
+   * Reverses the whole StringBuffer/Builder
+   */
+  public abstract void reverse(Object data);
+
+  /**
    * Returns the string buffer as a String.
    */
   public abstract String toString(Object data);
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java
index 6dd2ed5..15c97b3 100644
--- a/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java
@@ -73,7 +73,13 @@
   }
 
   @Override
+  public void reverse(Object data) {
+    string = reverseString(string);
+  }
+
+  @Override
   public String toString(Object data) {
     return string;
   }
+
 }
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java
index 227ef93..fd5600b 100644
--- a/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java
@@ -76,6 +76,13 @@
   }
 
   @Override
+  public void reverse(Object a) {
+    String s = takeString(a);
+    s = reverseString(s);
+    appendNonNull(a, s);
+  }
+
+  @Override
   public final String toString(Object a) {
     String s = takeString(a);
     appendNonNull(a, s);
@@ -87,4 +94,5 @@
     a.length = a.explicitLength = 0;
     return s;
   }-*/;
+
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
index 2b24557..b7b1a53 100644
--- a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
+++ b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
@@ -231,6 +231,11 @@
     return this;
   }
 
+  public StringBuffer reverse() {
+    impl.reverse(data);
+    return this;
+  }
+
   /**
    * Warning! This method is <b>much</b> slower than the JRE implementation. If
    * you need to do character level manipulation, you are strongly advised to
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
index e6d495a..37eb242 100644
--- a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
+++ b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
@@ -231,6 +231,11 @@
     return this;
   }
 
+  public StringBuilder reserve() {
+    impl.reverse(data);
+    return this;
+  }
+
   /**
    * Warning! This method is <b>much</b> slower than the JRE implementation. If
    * you need to do character level manipulation, you are strongly advised to
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java b/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java
index e92c177..fa0e807 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java
@@ -21,6 +21,11 @@
  * This class tests classes StringBuffer and StringBuilder.
  */
 public class StringBufferTest extends GWTTestCase {
+
+  private static void assertEqualsReverse(String expected, String input) {
+    assertEquals(expected, new StringBuffer(input).reverse().toString());
+  }
+
   /**
    * This method gets the module name.
    * 
@@ -270,6 +275,35 @@
     assertEquals("xxYYxx", x.toString());
   }
 
+  public void testReverse() {
+    assertEqualsReverse("", "");
+    assertEqualsReverse(" ", " ");
+
+    assertEqualsReverse("gwt", "twg");
+    assertEqualsReverse("gwt is great", "taerg si twg");
+
+    // Java uses UTF-16
+    // char values between 0x0000 - 0xD7FF and 0xE000 - 0xFFFF
+    // are considered to be one char
+    // if the value is between 0xD800 - 0xDBFF the char is considered
+    // to be the lead surrogate (first) of a surrogate pair
+    // if the value is between 0xDC00 - 0xDFFFF the char is considered
+    // to be the trail surrogate (second) of a surrogate pair
+    //
+    // single appearances of surrogates are invalid in a String!
+
+    // surrogate pair test (first one)
+    assertEqualsReverse("\uD800\uDC00", "\uD800\uDC00");
+    // surrogate pair test (last one)
+    assertEqualsReverse("\uDBFF\uDFFF", "\uDBFF\uDFFF");
+    // last one before surrogates start
+    assertEqualsReverse("\uD7FF\uD7FE", "\uD7FE\uD7FF");
+    // first none surrogate
+    assertEqualsReverse("\uE000\uE001", "\uE001\uE000");
+    // more than one surrogate pair
+    assertEqualsReverse("\uD801\uDC00_123_\uD802\uDC01", "\uD802\uDC01_321_\uD801\uDC00");
+  }
+
   /**
    * This method tests <code>setLength</code>.
    */