Expose the locale.queryparam and locale.cookie config params to client
code.  This allows things like a locale selector widget that is
automatically configured based on the module configuration.

Public review at: http://gwt-code-reviews.appspot.com/1250801/show

Review by: conroy, rjrjr, jlabanca


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9497 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml b/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml
index c1a4321..25d20b6 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml
@@ -4,6 +4,7 @@
   <inherits name='com.google.gwt.user.User'/>
   <inherits name="com.google.gwt.i18n.I18N"/>
   <inherits name="com.google.gwt.i18n.CldrLocales"/>
+  <inherits name="com.google.gwt.i18n.LocaleConfigLinker"/>
   <inherits name="com.google.gwt.user.theme.standard.StandardResources"/>
 
   <!-- Enable debug ID. -->
@@ -20,8 +21,11 @@
 
   <!-- Internationalization support. -->
   <extend-property name="locale" values="en"/>
+  <!-- 
   <extend-property name="locale" values="ar"/>
   <extend-property name="locale" values="fr"/>
   <extend-property name="locale" values="zh"/>
+   -->
   <set-property-fallback name="locale" value="en"/>
+  <set-configuration-property name="locale.cookie" value="SHOWCASE_LOCALE"/>
 </module>
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseShell.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseShell.java
index 418c02d..b24df5c 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseShell.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseShell.java
@@ -18,6 +18,7 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.TableCellElement;
 import com.google.gwt.dom.client.TableElement;
+import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.event.dom.client.ChangeEvent;
 import com.google.gwt.event.dom.client.ChangeHandler;
 import com.google.gwt.event.dom.client.ClickEvent;
@@ -26,12 +27,13 @@
 import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.http.client.UrlBuilder;
-import com.google.gwt.i18n.client.HasDirection.Direction;
 import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.i18n.client.HasDirection.Direction;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.cellview.client.CellTree;
 import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.client.Cookies;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.Window.Location;
 import com.google.gwt.user.client.ui.AbstractImagePrototype;
@@ -43,6 +45,7 @@
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwt.view.client.TreeViewModel;
 
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -305,6 +308,13 @@
    * Initialize the {@link ListBox} used for locale selection.
    */
   private void initializeLocaleBox() {
+    final String cookieName = LocaleInfo.getLocaleCookieName();
+    final String queryParam = LocaleInfo.getLocaleQueryParam();
+    if (cookieName == null && queryParam == null) {
+      // if there is no way for us to affect the locale, don't show the selector
+      localeSelectionCell.getStyle().setDisplay(Display.NONE);
+      return;
+    }
     String currentLocale = LocaleInfo.getCurrentLocale().getLocaleName();
     if (currentLocale.equals("default")) {
       currentLocale = "en";
@@ -320,11 +330,23 @@
       }
     }
     localeBox.addChangeHandler(new ChangeHandler() {
+      @SuppressWarnings("deprecation")
       public void onChange(ChangeEvent event) {
         String localeName = localeBox.getValue(localeBox.getSelectedIndex());
-        UrlBuilder builder = Location.createUrlBuilder().setParameter(
-            "locale", localeName);
-        Window.Location.replace(builder.buildString());
+        if (cookieName != null) {
+          // expire in one year
+          Date expires = new Date();
+          expires.setYear(expires.getYear() + 1);
+          Cookies.setCookie(cookieName, localeName, expires);
+        }
+        if (queryParam != null) {
+          UrlBuilder builder = Location.createUrlBuilder().setParameter(
+              queryParam, localeName);
+          Window.Location.replace(builder.buildString());
+        } else {
+          // If we are using only cookies, just reload
+          Window.Location.reload();
+        }
       }
     });
   }
diff --git a/user/src/com/google/gwt/i18n/client/LocaleInfo.java b/user/src/com/google/gwt/i18n/client/LocaleInfo.java
index ff188cf..7f1ed32 100644
--- a/user/src/com/google/gwt/i18n/client/LocaleInfo.java
+++ b/user/src/com/google/gwt/i18n/client/LocaleInfo.java
@@ -69,6 +69,16 @@
   }
 
   /**
+   * Returns the name of the name of the cookie holding the locale to use,
+   * which is defined in the config property {@code locale.cookie}.
+   * 
+   * @return locale cookie name, or null if none
+   */
+  public static final String getLocaleCookieName() {
+    return instance.infoImpl.getLocaleCookieName();
+  }
+
+  /**
    * Returns the display name of the requested locale in its native locale, if
    * possible. If no native localization is available, the English name will
    * be returned, or as a last resort just the locale name will be returned.  If
@@ -88,6 +98,16 @@
   }
 
   /**
+   * Returns the name of the query parameter holding the locale to use, which is
+   * defined in the config property {@code locale.queryparam}.
+   * 
+   * @return locale URL query parameter name, or null if none
+   */
+  public static String getLocaleQueryParam() {
+    return instance.infoImpl.getLocaleQueryParam();
+  }
+
+  /**
    * Returns true if any locale supported by this build of the app is RTL.
    */
   public static boolean hasAnyRTL() {
@@ -150,7 +170,7 @@
   /**
    * @return an implementation of {@link LocalizedNames} for this locale.
    */
-  public LocalizedNames getLocalizedNames() {
+  public final LocalizedNames getLocalizedNames() {
     return infoImpl.getLocalizedNames();
   }
 
diff --git a/user/src/com/google/gwt/i18n/client/impl/LocaleInfoImpl.java b/user/src/com/google/gwt/i18n/client/impl/LocaleInfoImpl.java
index 92d5ee8..872c285 100644
--- a/user/src/com/google/gwt/i18n/client/impl/LocaleInfoImpl.java
+++ b/user/src/com/google/gwt/i18n/client/impl/LocaleInfoImpl.java
@@ -62,6 +62,16 @@
   }
 
   /**
+   * Returns the name of the name of the cookie holding the locale to use,
+   * which is defined in the config property {@code locale.cookie}.
+   * 
+   * @return locale cookie name, or null if none
+   */
+  public String getLocaleCookieName() {
+    return null;
+  }
+
+  /**
    * Returns the current locale name, such as "default, "en_US", etc.
    */
   public String getLocaleName() {
@@ -82,6 +92,16 @@
   }
 
   /**
+   * Returns the name of the query parameter holding the locale to use, which is
+   * defined in the config property {@code locale.queryparam}.
+   * 
+   * @return locale URL query parameter name, or null if none
+   */
+  public String getLocaleQueryParam() {
+    return null;
+  }
+
+  /**
    * @return an implementation of {@link LocalizedNames} for this locale.
    */
   public LocalizedNames getLocalizedNames() {
diff --git a/user/src/com/google/gwt/i18n/rebind/LocaleInfoContext.java b/user/src/com/google/gwt/i18n/rebind/LocaleInfoContext.java
index c3dfd4c..59053eb 100644
--- a/user/src/com/google/gwt/i18n/rebind/LocaleInfoContext.java
+++ b/user/src/com/google/gwt/i18n/rebind/LocaleInfoContext.java
@@ -25,23 +25,32 @@
  * A LocaleUtils specific context for caching.
  */
 public class LocaleInfoContext {
+
   /**
    * A key for lookup of computed values in a cache.
    */
   private static class CacheKey {
     private final SelectionProperty localeProperty;
     private final ConfigurationProperty runtimeLocaleProperty;
+    private ConfigurationProperty queryParamProperty;
+    private ConfigurationProperty cookieProperty;
 
     /**
      * Create a key for cache lookup.
      * 
      * @param localeProperty "locale" property, must not be null
      * @param runtimeLocaleProperty "runtime.locales" property, must not be null
+     * @param cookieProperty "locale.queryparam" property, must not be null
+     * @param queryParamProperty "locale.cookie" property, must not be null
      */
     public CacheKey(SelectionProperty localeProperty,
-        ConfigurationProperty runtimeLocaleProperty) {
+        ConfigurationProperty runtimeLocaleProperty,
+        ConfigurationProperty queryParamProperty,
+        ConfigurationProperty cookieProperty) {
       this.localeProperty = localeProperty;
       this.runtimeLocaleProperty = runtimeLocaleProperty;
+      this.queryParamProperty = queryParamProperty;
+      this.cookieProperty = cookieProperty;
     }
 
     @Override
@@ -57,7 +66,9 @@
       }
       CacheKey other = (CacheKey) obj;
       return localeProperty.equals(other.localeProperty)
-          && runtimeLocaleProperty.equals(other.runtimeLocaleProperty);
+          && runtimeLocaleProperty.equals(other.runtimeLocaleProperty)
+          && queryParamProperty.equals(other.queryParamProperty)
+          && cookieProperty.equals(other.cookieProperty);
     }
 
     @Override
@@ -66,6 +77,8 @@
       int result = 1;
       result = prime * result + localeProperty.hashCode();
       result = prime * result + runtimeLocaleProperty.hashCode();
+      result = prime * result + queryParamProperty.hashCode();
+      result = prime * result + cookieProperty.hashCode();
       return result;
     }
   }
@@ -74,15 +87,18 @@
       CacheKey, LocaleUtils>();
 
   public LocaleUtils getLocaleUtils(SelectionProperty localeProperty,
-      ConfigurationProperty runtimeLocaleProperty) {
-    CacheKey key = new CacheKey(localeProperty, runtimeLocaleProperty);
+      ConfigurationProperty runtimeLocaleProperty,
+      ConfigurationProperty queryParamProp, ConfigurationProperty cookieProp) {
+    CacheKey key = new CacheKey(localeProperty, runtimeLocaleProperty,
+        queryParamProp, cookieProp);
     return localeUtilsCache.get(key);
   }
-  
+
   public void putLocaleUtils(SelectionProperty localeProperty,
-      ConfigurationProperty runtimeLocaleProperty, LocaleUtils localeUtils) {
-    CacheKey key = new CacheKey(localeProperty, runtimeLocaleProperty);
+      ConfigurationProperty runtimeLocaleProperty, ConfigurationProperty queryParamProp,
+      ConfigurationProperty cookieProp, LocaleUtils localeUtils) {
+    CacheKey key = new CacheKey(localeProperty, runtimeLocaleProperty,
+        queryParamProp, cookieProp);
     localeUtilsCache.put(key, localeUtils);
   }
-
 }
diff --git a/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java b/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java
index a899a96..e4ba8d0 100644
--- a/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java
+++ b/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java
@@ -276,6 +276,22 @@
       }
       writer.println("}");
       writer.println();
+      String queryParam = localeUtils.getQueryParam();
+      if (queryParam != null) {
+        writer.println("@Override");
+        writer.println("public String getLocaleQueryParam() {");
+        writer.println("  return \"" + quoteQuotes(queryParam) + "\";");
+        writer.println("}");
+        writer.println();
+      }
+      String cookie = localeUtils.getCookie();
+      if (cookie != null) {
+        writer.println("@Override");
+        writer.println("public String getLocaleCookieName() {");
+        writer.println("  return \"" + quoteQuotes(cookie) + "\";");
+        writer.println("}");
+        writer.println();
+      }
       writer.println("@Override");
       writer.println("public DateTimeFormatInfo getDateTimeFormatInfo() {");
       LocalizableGenerator localizableGenerator = new LocalizableGenerator();
diff --git a/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java b/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java
index eee04b0..8995221 100644
--- a/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java
+++ b/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java
@@ -44,6 +44,18 @@
   private static final String PROP_LOCALE = "locale";
 
   /**
+   * The config property identifying the URL query paramter name to possibly get
+   * the value of the locale property.
+   */
+  private static final String PROP_LOCALE_QUERY_PARAM = "locale.queryparam";
+
+  /**
+   * The config property identifying the cookie name to possibly get the value
+   * of the locale property.
+   */
+  private static final String PROP_LOCALE_COOKIE = "locale.cookie";
+
+  /**
    * The token representing the runtime.locales configuration property.
    */
   private static final String PROP_RUNTIME_LOCALES = "runtime.locales";
@@ -70,11 +82,18 @@
           = propertyOracle.getSelectionProperty(logger, PROP_LOCALE);
       ConfigurationProperty runtimeLocaleProp
           = propertyOracle.getConfigurationProperty(PROP_RUNTIME_LOCALES);
+      ConfigurationProperty queryParamProp
+          = propertyOracle.getConfigurationProperty(PROP_LOCALE_QUERY_PARAM);
+      ConfigurationProperty cookieProp
+          = propertyOracle.getConfigurationProperty(PROP_LOCALE_COOKIE);
       LocaleInfoContext localeInfoCtx = getLocaleInfoCtx(context);
-      LocaleUtils localeUtils = localeInfoCtx.getLocaleUtils(localeProp, runtimeLocaleProp);
+      LocaleUtils localeUtils = localeInfoCtx.getLocaleUtils(localeProp,
+          runtimeLocaleProp, queryParamProp, cookieProp);
       if (localeUtils == null) {
-        localeUtils = createInstance(localeProp, runtimeLocaleProp);
-        localeInfoCtx.putLocaleUtils(localeProp, runtimeLocaleProp, localeUtils);
+        localeUtils = createInstance(localeProp, runtimeLocaleProp,
+            queryParamProp, cookieProp);
+        localeInfoCtx.putLocaleUtils(localeProp, runtimeLocaleProp,
+            queryParamProp, cookieProp, localeUtils);
       }
       return localeUtils;
     } catch (BadPropertyValueException e) {
@@ -85,7 +104,7 @@
       Set<GwtLocale> allLocales = new HashSet<GwtLocale>();
       allLocales.add(defaultLocale);
       return new LocaleUtils(defaultLocale, allLocales, allLocales,
-          Collections.<GwtLocale>emptySet());
+          Collections.<GwtLocale>emptySet(), null, null);
     }
   }
 
@@ -99,12 +118,21 @@
   }
 
   private static LocaleUtils createInstance(SelectionProperty localeProp,
-      ConfigurationProperty prop) {
+      ConfigurationProperty prop, ConfigurationProperty queryParamProp,
+      ConfigurationProperty cookieProp) {
     GwtLocale compileLocale = null;
     Set<GwtLocale> allLocales = new HashSet<GwtLocale>();
     Set<GwtLocale> allCompileLocales = new HashSet<GwtLocale>();
     Set<GwtLocale> runtimeLocales = new HashSet<GwtLocale>();
     String localeName = localeProp.getCurrentValue();
+    String queryParam = queryParamProp.getValues().get(0);
+    if (queryParam.length() == 0) {
+      queryParam = null;
+    }
+    String cookie = cookieProp.getValues().get(0);
+    if (cookie.length() == 0) {
+      cookie = null;
+    }
     SortedSet<String> localeValues = localeProp.getPossibleValues();
 
     GwtLocaleFactory factoryInstance = getLocaleFactory();
@@ -140,7 +168,7 @@
       }
     }
     return new LocaleUtils(compileLocale, allLocales, allCompileLocales,
-        runtimeLocales);
+        runtimeLocales, queryParam, cookie);
   }
 
   private static synchronized LocaleInfoContext getLocaleInfoCtx(
@@ -164,12 +192,19 @@
 
   private final Set<GwtLocale> runtimeLocales;
 
+  private final String queryParam;
+
+  private final String cookie;
+
   private LocaleUtils(GwtLocale compileLocale, Set<GwtLocale> allLocales,
-      Set<GwtLocale> allCompileLocales, Set<GwtLocale> runtimeLocales) {
+      Set<GwtLocale> allCompileLocales, Set<GwtLocale> runtimeLocales,
+      String queryParam, String cookie) {
     this.compileLocale = compileLocale;
     this.allLocales = Collections.unmodifiableSet(allLocales);
     this.allCompileLocales = Collections.unmodifiableSet(allCompileLocales);
     this.runtimeLocales = Collections.unmodifiableSet(runtimeLocales);
+    this.queryParam = queryParam;
+    this.cookie = cookie;
   }
 
   /**
@@ -199,6 +234,25 @@
   }
 
   /**
+   * Return the name of the cookie to potentially get the locale value from.
+   *
+   * @return the cookie name or null if none
+   */
+  public String getCookie() {
+    return cookie;
+  }
+
+  /**
+   * Return the name of the URL query param to potentially get the locale value
+   * from.
+   *
+   * @return the URL query param or null if none
+   */
+  public String getQueryParam() {
+    return queryParam;
+  }
+
+  /**
    * Returns a list of locales which are children of the current compile-time
    * locale.
    *
diff --git a/user/test/com/google/gwt/i18n/I18NTest_en.gwt.xml b/user/test/com/google/gwt/i18n/I18NTest_en.gwt.xml
index 280d4ef..f9d3cf5 100644
--- a/user/test/com/google/gwt/i18n/I18NTest_en.gwt.xml
+++ b/user/test/com/google/gwt/i18n/I18NTest_en.gwt.xml
@@ -13,12 +13,14 @@
 <!-- limitations under the License.                                         -->
 
 <module>
-	<!-- Inherit the JUnit support -->
-	<inherits name='com.google.gwt.junit.JUnit'/>
-	<inherits name = 'com.google.gwt.i18n.I18N'/>
-	<!-- Include client-side source for the test cases -->
-	<source path="client"/>
-	<extend-property name="locale" values="en_US"/>
-        <extend-property name="locale" values="ar"/>
-	<set-property name = "locale" value = "en_US"/>
+  <!-- Inherit the JUnit support -->
+  <inherits name='com.google.gwt.junit.JUnit'/>
+  <inherits name = 'com.google.gwt.i18n.I18N'/>
+  <!-- Include client-side source for the test cases -->
+  <source path="client"/>
+  <extend-property name="locale" values="en_US"/>
+  <extend-property name="locale" values="ar"/>
+  <set-property name = "locale" value = "en_US"/>
+  <set-configuration-property name="locale.queryparam" value=""/>
+  <set-configuration-property name="locale.cookie" value="LOCALE"/>
 </module>
diff --git a/user/test/com/google/gwt/i18n/client/LocaleInfoTest.java b/user/test/com/google/gwt/i18n/client/LocaleInfoTest.java
index 52aa6db..787491f 100644
--- a/user/test/com/google/gwt/i18n/client/LocaleInfoTest.java
+++ b/user/test/com/google/gwt/i18n/client/LocaleInfoTest.java
@@ -33,6 +33,11 @@
         "default", "piglatin", "piglatin_UK", "piglatin_UK_WINDOWS"}, locales);
   }
 
+  public void testCookieName() {
+    String cookieName = LocaleInfo.getCurrentLocale().getLocaleCookieName();
+    assertNull(cookieName);
+  }
+
   public void testCurrentLocale() {
     String locale = LocaleInfo.getCurrentLocale().getLocaleName();
     assertEquals("piglatin_UK_WINDOWS", locale);
@@ -47,6 +52,11 @@
     displayName = LocaleInfo.getLocaleNativeDisplayName("piglatin");
     assertEquals("Igpay Atinlay", displayName);
   }
+
+  public void testQueryParam() {
+    String queryParam = LocaleInfo.getCurrentLocale().getLocaleQueryParam();
+    assertEquals("locale", queryParam);
+  }
   
   public void testRTL() {
     boolean isRTL = LocaleInfo.getCurrentLocale().isRTL();
diff --git a/user/test/com/google/gwt/i18n/client/LocaleInfo_en_Test.java b/user/test/com/google/gwt/i18n/client/LocaleInfo_en_Test.java
index 4dfc5d3..8cb6e7b 100644
--- a/user/test/com/google/gwt/i18n/client/LocaleInfo_en_Test.java
+++ b/user/test/com/google/gwt/i18n/client/LocaleInfo_en_Test.java
@@ -27,7 +27,8 @@
 
   @Override
   public String getModuleName() {
-    // This module is built in the en locale, but includes ar.
+    // This module is built in the en locale, but includes ar.  It also has
+    // custom settings for the locale query parameter and cookie name.
     return "com.google.gwt.i18n.I18NTest_en";
   }
 
@@ -40,6 +41,16 @@
     assertTrue(localeList.contains("en_US"));
   }
 
+  public void testCookieName() {
+    String cookieName = LocaleInfo.getCurrentLocale().getLocaleCookieName();
+    assertEquals("LOCALE", cookieName);
+  }
+
+  public void testQueryParam() {
+    String queryParam = LocaleInfo.getCurrentLocale().getLocaleQueryParam();
+    assertNull(queryParam);
+  }
+
   public void testRTL() {
     boolean isRTL = LocaleInfo.getCurrentLocale().isRTL();
     assertFalse(isRTL);