Introduces Locale overload for toUpperCase/toLowerCase.

This patch introduces a simple Locale emulation that defines the constants
for common Locale classes used by shared code to avoid locale specific
String transformations (e.g. String.toLowerCase(Locale.US)).

Also adds Locale overloads to toUpperCase/toLowerCase so it does the right
thing when the locale is passed. More specifically calls
 - javascript toXXXCase if Locale is US, ENGLISH and ROOT.
 - javascript toLocaleXXXCase if Locale is Locale#getDefault()

By this way, shared code can perform both locale specific and non-locale
specific String transformations that works cross-platform (given that it's
supported by the underlying browser).

Change-Id: I753c61ccf46f441afeee542f88171bd47e0d973f
Review-Link: https://gwt-review.googlesource.com/#/c/8991/
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java
index 2ed5862..4f9c2dc 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -23,6 +23,7 @@
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 import java.util.Comparator;
+import java.util.Locale;
 
 /**
  * Intrinsic string class.
@@ -965,10 +966,50 @@
     return charArr;
   }
 
+  /**
+   * Transforms the String to lower-case in a locale insensitive way.
+   * <p>
+   * Unlike JRE, we don't do locale specific transformation by default. That is backward compatible
+   * for GWT and in most of the cases that is what the developer actually wants. If you want to make
+   * a transformation based on native locale of the browser, you can do
+   * {@code toLowerCase(Locale.getDefault())} instead.
+   */
   public native String toLowerCase() /*-{
     return this.toLowerCase();
   }-*/;
 
+  /**
+   * Transforms the String to lower-case based on the native locale of the browser.
+   */
+  private native String toLocaleLowerCase() /*-{
+    return this.toLocaleLowerCase();
+  }-*/;
+
+  /**
+   * If provided {@code locale} is {@link Locale#getDefault()}, uses javascript's
+   * {@code toLocaleLowerCase} to do a locale specific transformation. Otherwise, it will fallback
+   * to {@code toLowerCase} which performs the right thing for the limited set of Locale's
+   * predefined in GWT Locale emulation.
+   */
+  public String toLowerCase(Locale locale) {
+    return locale == Locale.getDefault() ? toLocaleLowerCase() : toLowerCase();
+  }
+
+  // See the notes in lowerCase pair.
+  public native String toUpperCase() /*-{
+    return this.toUpperCase();
+  }-*/;
+
+  // See the notes in lowerCase pair.
+  private native String toLocaleUpperCase() /*-{
+    return this.toLocaleUpperCase();
+  }-*/;
+
+  // See the notes in lowerCase pair.
+  public String toUpperCase(Locale locale) {
+    return locale == Locale.getDefault() ? toLocaleUpperCase() : toUpperCase();
+  }
+
   @Override
   public String toString() {
     /*
@@ -978,10 +1019,6 @@
     return this;
   }
 
-  public native String toUpperCase() /*-{
-    return this.toUpperCase();
-  }-*/;
-
   public native String trim() /*-{
     if (this.length == 0 || (this[0] > '\u0020' && this[this.length - 1] > '\u0020')) {
       return this;
diff --git a/user/super/com/google/gwt/emul/java/util/Locale.java b/user/super/com/google/gwt/emul/java/util/Locale.java
new file mode 100644
index 0000000..48606e9
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/Locale.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util;
+
+/**
+ * A very simple emulation of Locale for shared-code patterns like
+ * {@code String.toUpperCase(Locale.US)}.
+ * <p>
+ * Note: Any changes to this class should put into account the assumption that was made in rest of
+ * the JRE emulation.
+ */
+public class Locale {
+
+  public static final Locale ROOT = new Locale() {
+    @Override
+    public String toString() {
+      return "";
+    }
+  };
+
+  public static final Locale ENGLISH = new Locale() {
+    @Override
+    public String toString() {
+      return "en";
+    }
+  };
+
+  public static final Locale US = new Locale() {
+    @Override
+    public String toString() {
+      return "en_US";
+    }
+  };
+
+  private static Locale defaultLocale = new Locale() {
+    @Override
+    public String toString() {
+      return "unknown";
+    }
+  };
+
+  /**
+   * Returns an instance that represents the browser's default locale (not necessarily the one
+   * defined by 'gwt.locale').
+   */
+  public static Locale getDefault() {
+    return defaultLocale;
+  }
+
+  // Hidden as we don't support manual creation of Locales.
+  private Locale() { }
+}
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringTest.java b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
index 1d81463..41af5de 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
@@ -21,6 +21,7 @@
 import com.google.gwt.junit.client.GWTTestCase;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Locale;
 
 /**
  * TODO: COMPILER OPTIMIZATIONS HAVE MADE THIS TEST NOT ACTUALLY TEST ANYTHING!
@@ -453,9 +454,17 @@
    * TODO: needs rewriting to avoid compiler optimizations.
    */
   public void testLowerCase() {
-    assertEquals("abc", StringCase.toLower("AbC"));
-    assertEquals("abc", StringCase.toLower("abc"));
-    assertEquals("", StringCase.toLower(""));
+    assertEquals("abc", "AbC".toLowerCase());
+    assertEquals("abc", "abc".toLowerCase());
+    assertEquals("", "".toLowerCase());
+
+    assertEquals("abc", "AbC".toLowerCase(Locale.US));
+    assertEquals("abc", "abc".toLowerCase(Locale.US));
+    assertEquals("", "".toLowerCase(Locale.US));
+
+    assertEquals("abc", "AbC".toLowerCase(Locale.getDefault()));
+    assertEquals("abc", "abc".toLowerCase(Locale.getDefault()));
+    assertEquals("", "".toLowerCase(Locale.getDefault()));
   }
 
   public void testMatch() {
@@ -705,9 +714,17 @@
    * TODO: needs rewriting to avoid compiler optimizations.
    */
   public void testUpperCase() {
-    assertEquals("abc", StringCase.toLower("AbC"));
-    assertEquals("abc", StringCase.toLower("abc"));
-    assertEquals("", StringCase.toLower(""));
+    assertEquals("ABC", "AbC".toUpperCase());
+    assertEquals("ABC", "abc".toUpperCase());
+    assertEquals("", "".toUpperCase());
+
+    assertEquals("ABC", "AbC".toUpperCase(Locale.US));
+    assertEquals("ABC", "abc".toUpperCase(Locale.US));
+    assertEquals("", "".toUpperCase(Locale.US));
+
+    assertEquals("ABC", "AbC".toUpperCase(Locale.getDefault()));
+    assertEquals("ABC", "abc".toUpperCase(Locale.getDefault()));
+    assertEquals("", "".toUpperCase(Locale.getDefault()));
   }
 
   /*