Revision: 6927
Author: [email protected]
Date: Mon Nov 16 13:33:45 2009
Log: Merge trunk r6926 into this branch

Fix thread safety issues in GwtLocaleFactoryImpl and LocaleUtils.

     svn merge --ignore-ancestry -c6926 \
       https://google-web-toolkit.googlecode.com/svn/trunk/ .


http://code.google.com/p/google-web-toolkit/source/detail?r=6927

Added:
   
/releases/2.0/dev/core/src/com/google/gwt/core/ext/DefaultConfigurationProperty.java
   
/releases/2.0/dev/core/src/com/google/gwt/core/ext/DefaultSelectionProperty.java
Modified:
  /releases/2.0/branch-info.txt
  /releases/2.0/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java
  /releases/2.0/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
  /releases/2.0/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java
   
/releases/2.0/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
   
/releases/2.0/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java
  /releases/2.0/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java
  /releases/2.0/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java
  /releases/2.0/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java
  /releases/2.0/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java

=======================================
--- /dev/null
+++  
/releases/2.0/dev/core/src/com/google/gwt/core/ext/DefaultConfigurationProperty.java
     
Mon Nov 16 13:33:45 2009
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2009 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 com.google.gwt.core.ext;
+
+import java.util.List;
+
+/**
+ * Default immutable implementation of ConfigurationProperty that receives  
its
+ * values in its constructor.
+ */
+public class DefaultConfigurationProperty implements ConfigurationProperty  
{
+
+  private final String name;
+  private final List<String> values;
+
+  /**
+   * Construct a configuration property.
+   *
+   * @param name the name of this property, must not be null
+   * @param values the list of possible values, must not be null and
+   *     will be returned to callers, so a copy should be passed into this
+   *     ctor if the caller will use this set later
+   */
+  public DefaultConfigurationProperty(String name, List<String> values) {
+    assert name != null;
+    assert values != null;
+    this.name = name;
+    this.values = values;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    DefaultConfigurationProperty other = (DefaultConfigurationProperty)  
obj;
+    return name.equals(other.name)
+        && values.equals(other.values);
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public List<String> getValues() {
+    return values;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + name.hashCode();
+    result = prime * result + values.hashCode();
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "ConfigProp " + name + ": " + values.toString();
+  }
+}
=======================================
--- /dev/null
+++  
/releases/2.0/dev/core/src/com/google/gwt/core/ext/DefaultSelectionProperty.java
         
Mon Nov 16 13:33:45 2009
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2009 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 com.google.gwt.core.ext;
+
+import java.util.SortedSet;
+
+/**
+ * Default immutable implementation of SelectionProperty that receives its
+ * values in its constructor.
+ */
+public class DefaultSelectionProperty implements SelectionProperty {
+
+  private final String currentValue;
+  private final String fallbackValue;
+  private final String name;
+  private final SortedSet<String> possibleValues;
+
+  /**
+   * Construct a selection property.
+   *
+   * @param currentValue current value of this property, must not be null
+   * @param fallbackValue the fallback value to use, must not be null
+   * @param name the name of this property, must not be null
+   * @param possibleValues the set of possible values, must not be null and
+   *     will be returned to callers, so a copy should be passed into this
+   *     ctor if the caller will use this set later
+   */
+  public DefaultSelectionProperty(String currentValue, String  
fallbackValue,
+      String name, SortedSet<String> possibleValues) {
+    assert currentValue != null;
+    assert fallbackValue != null;
+    assert name != null;
+    assert possibleValues != null;
+    this.currentValue = currentValue;
+    this.fallbackValue = fallbackValue;
+    this.name = name;
+    this.possibleValues = possibleValues;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    DefaultSelectionProperty other = (DefaultSelectionProperty) obj;
+    return currentValue.equals(other.currentValue)
+        && fallbackValue.equals(other.fallbackValue)
+        && name.equals(other.name)
+        && possibleValues.equals(other.possibleValues);
+  }
+
+  public String getCurrentValue() {
+    return currentValue;
+  }
+
+  public String getFallbackValue() {
+    return fallbackValue;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public SortedSet<String> getPossibleValues() {
+    return possibleValues;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + currentValue.hashCode();
+    result = prime * result + fallbackValue.hashCode();
+    result = prime * result + name.hashCode();
+    result = prime * result + possibleValues.hashCode();
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "SelectionProp " + name + ": " + currentValue + " of "
+        + possibleValues.toString() + ", fallback =" + fallbackValue;
+  }
+}
=======================================
--- /releases/2.0/branch-info.txt       Mon Nov 16 12:57:41 2009
+++ /releases/2.0/branch-info.txt       Mon Nov 16 13:33:45 2009
@@ -692,3 +692,7 @@
    Allows path to executable in RunStyleSelenium.
    svn merge --ignore-ancestry -c6923  
https://google-web-toolkit.googlecode.com/svn/trunk .

+tr...@6926 was merged into this branhc
+ Fix thread safety issues in GwtLocaleFactoryImpl and LocaleUtils.
+    svn merge --ignore-ancestry -c6926 \
+      https://google-web-toolkit.googlecode.com/svn/trunk/ .
=======================================
--- /releases/2.0/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java      
 
Tue May  5 13:08:14 2009
+++ /releases/2.0/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java      
 
Mon Nov 16 13:33:45 2009
@@ -25,6 +25,11 @@
     * <code>BadPropertyValueException</code> if the property is undefined.  
The
     * result of invoking this method with the same  
<code>propertyName</code> must
     * be stable.
+   *
+   * @param propertyName
+   * @return the configuration property instance (never null)
+   * @throws BadPropertyValueException if the property is unknown or not a
+   *     configuration property
     */
    ConfigurationProperty getConfigurationProperty(String propertyName)
        throws BadPropertyValueException;
@@ -38,6 +43,8 @@
     * @param logger the current logger
     * @param propertyName the name of the property
     * @return a value for the property
+   * @throws BadPropertyValueException if the property is unknown or not  
of the
+   *     right type
     */
    @Deprecated
    String getPropertyValue(TreeLogger logger, String propertyName)
@@ -53,6 +60,8 @@
     * @param logger the current logger
     * @param propertyName the name of the property
     * @return the possible values for the property
+   * @throws BadPropertyValueException if the property is unknown or not  
of the
+   *     right type
     */
    @Deprecated
    String[] getPropertyValueSet(TreeLogger logger, String propertyName)
@@ -63,6 +72,11 @@
     * <code>BadPropertyValueException</code> if the property is either  
undefined
     * or has a value that is unsupported. The result of invoking this  
method with
     * the same <code>propertyName</code> must be stable.
+   * @param logger
+   * @param propertyName
+   * @return the selection property instance (never null)
+   * @throws BadPropertyValueException if the property is unknown or not a
+   *     selection property
     */
    SelectionProperty getSelectionProperty(TreeLogger logger, String  
propertyName)
        throws BadPropertyValueException;
=======================================
---  
/releases/2.0/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java     
 
Tue Jun 16 14:20:27 2009
+++  
/releases/2.0/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java     
 
Mon Nov 16 13:33:45 2009
@@ -16,12 +16,12 @@
  package com.google.gwt.dev.cfg;

  import com.google.gwt.core.ext.BadPropertyValueException;
+import com.google.gwt.core.ext.DefaultConfigurationProperty;
+import com.google.gwt.core.ext.DefaultSelectionProperty;
  import com.google.gwt.core.ext.PropertyOracle;
  import com.google.gwt.core.ext.TreeLogger;

  import java.io.Serializable;
-import java.util.List;
-import java.util.SortedSet;
  import java.util.TreeSet;

  /**
@@ -36,6 +36,13 @@

    private final String[] orderedPropValues;

+  /**
+   * Create a property oracle that will return the supplied values.
+   *
+   * @param orderedProps array of binding properties
+   * @param orderedPropValues values of the above binding properties
+   * @param configProps array of config properties
+   */
    public StaticPropertyOracle(BindingProperty[] orderedProps,
        String[] orderedPropValues, ConfigurationProperty[] configProps) {
      this.orderedProps = orderedProps;
@@ -58,24 +65,23 @@
        String propertyName) throws BadPropertyValueException {
      for (final ConfigurationProperty prop : configProps) {
        if (prop.getName().equals(propertyName)) {
-        return new com.google.gwt.core.ext.ConfigurationProperty() {
-          public String getName() {
-            return prop.getName();
-          }
-
-          public List<String> getValues() {
-            return prop.getValues();
-          }
-        };
+        return new DefaultConfigurationProperty(prop.getName(),
+            prop.getValues());
        }
      }
      throw new BadPropertyValueException(propertyName);
    }

+  /**
+   * @return an array of binding properties.
+   */
    public BindingProperty[] getOrderedProps() {
      return orderedProps;
    }

+  /**
+   * @return an array of binding property values.
+   */
    public String[] getOrderedPropValues() {
      return orderedPropValues;
    }
@@ -138,7 +144,6 @@
      for (int i = 0; i < orderedProps.length; i++) {
        final BindingProperty prop = orderedProps[i];
        final String name = prop.getName();
-      final String fallback = prop.getFallback();
        if (name.equals(propertyName)) {
          final String value = orderedPropValues[i];
          String[] values = prop.getDefinedValues();
@@ -146,24 +151,8 @@
          for (String v : values) {
            possibleValues.add(v);
          }
-
-        return new com.google.gwt.core.ext.SelectionProperty() {
-          public String getCurrentValue() {
-            return value;
-          }
-
-          public String getFallbackValue() {
-            return fallback;
-          }
-
-          public String getName() {
-            return name;
-          }
-
-          public SortedSet<String> getPossibleValues() {
-            return possibleValues;
-          }
-        };
+        return new DefaultSelectionProperty(value, prop.getFallback(),  
name,
+            possibleValues);
        }
      }

=======================================
---  
/releases/2.0/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java       
 
Fri Oct 16 20:54:44 2009
+++  
/releases/2.0/dev/core/src/com/google/gwt/dev/shell/ModuleSpaceOOPHM.java       
 
Mon Nov 16 13:33:45 2009
@@ -100,7 +100,7 @@
    @Override
    protected JsValue doInvoke(String name, Object jthis, Class<?>[] types,
        Object[] args) throws Throwable {
-    TreeLogger branch = host.getLogger().branch(TreeLogger.DEBUG,
+    TreeLogger branch = host.getLogger().branch(TreeLogger.SPAM,
          "Invoke native method " + name, null);
      CompilingClassLoader isolatedClassLoader = getIsolatedClassLoader();
      JsValueOOPHM jsthis = new JsValueOOPHM();
@@ -121,7 +121,7 @@
            returnVal);
        branch.log(TreeLogger.SPAM, "  returned " + returnVal);
      } catch (Throwable t) {
-      branch.log(TreeLogger.DEBUG, "exception thrown", t);
+      branch.log(TreeLogger.SPAM, "exception thrown", t);
        throw t;
      }
      return returnVal;
=======================================
---  
/releases/2.0/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
       
Mon Aug 17 09:47:12 2009
+++  
/releases/2.0/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
       
Mon Nov 16 13:33:45 2009
@@ -16,6 +16,8 @@
  package com.google.gwt.dev.shell;

  import com.google.gwt.core.ext.BadPropertyValueException;
+import com.google.gwt.core.ext.DefaultConfigurationProperty;
+import com.google.gwt.core.ext.DefaultSelectionProperty;
  import com.google.gwt.core.ext.PropertyOracle;
  import com.google.gwt.core.ext.SelectionProperty;
  import com.google.gwt.core.ext.TreeLogger;
@@ -45,6 +47,12 @@

    private final ModuleSpace space;

+  /**
+   * Create a property oracle that computes its properties from a module.
+   *
+   * @param props
+   * @param space
+   */
    public ModuleSpacePropertyOracle(Properties props, ModuleSpace space) {
      this.space = space;
      this.props = props;
@@ -57,15 +65,7 @@
        final ConfigurationProperty cprop = (ConfigurationProperty) prop;
        final String name = cprop.getName();
        final List<String> values = cprop.getValues();
-      return new com.google.gwt.core.ext.ConfigurationProperty() {
-        public String getName() {
-          return name;
-        }
-
-        public List<String> getValues() {
-          return values;
-        }
-      };
+      return new DefaultConfigurationProperty(name, values);
      } else {
        throw new BadPropertyValueException(propertyName);
      }
@@ -132,24 +132,8 @@
        for (String v : cprop.getDefinedValues()) {
          possibleValues.add(v);
        }
-      return new com.google.gwt.core.ext.SelectionProperty() {
-
-        public String getCurrentValue() {
-          return value;
-        }
-
-        public String getFallbackValue() {
-          return fallback;
-        }
-
-        public String getName() {
-          return name;
-        }
-
-        public SortedSet<String> getPossibleValues() {
-          return possibleValues;
-        }
-      };
+      return new DefaultSelectionProperty(value, fallback, name,
+          possibleValues);
      } else {
        throw new BadPropertyValueException(propertyName);
      }
=======================================
---  
/releases/2.0/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java    
 
Tue Jun 23 20:13:51 2009
+++  
/releases/2.0/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java    
 
Mon Nov 16 13:33:45 2009
@@ -258,9 +258,9 @@
      TypeOracle typeOracle = context.getTypeOracle();

      PropertyOracle propertyOracle = context.getPropertyOracle();
-    LocaleUtils.init(logger, propertyOracle);
-    GwtLocale locale = LocaleUtils.getCompileLocale();
-    Set<GwtLocale> runtimeLocales = LocaleUtils.getRuntimeLocales();
+    LocaleUtils localeUtils = LocaleUtils.getInstance(logger,  
propertyOracle);
+    GwtLocale locale = localeUtils.getCompileLocale();
+    Set<GwtLocale> runtimeLocales = localeUtils.getRuntimeLocales();

      JClassType targetClass;
      try {
=======================================
---  
/releases/2.0/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java      
 
Mon May 18 11:47:32 2009
+++  
/releases/2.0/user/src/com/google/gwt/i18n/rebind/LocaleInfoGenerator.java      
 
Mon Nov 16 13:33:45 2009
@@ -81,7 +81,8 @@
      TypeOracle typeOracle = context.getTypeOracle();
      // Get the current locale and interface type.
      PropertyOracle propertyOracle = context.getPropertyOracle();
-    LocaleUtils.init(logger, propertyOracle);
+    LocaleUtils localeUtils = LocaleUtils.getInstance(logger,
+        propertyOracle);

      JClassType targetClass;
      try {
@@ -90,12 +91,14 @@
        logger.log(TreeLogger.ERROR, "No such type " + typeName, e);
        throw new UnableToCompleteException();
      }
-    assert  
(LocaleInfoImpl.class.getName().equals(targetClass.getQualifiedSourceName()));
+    assert (LocaleInfoImpl.class.getName().equals(
+        targetClass.getQualifiedSourceName()));

      String packageName = targetClass.getPackage().getName();
      String superClassName = targetClass.getName().replace('.', '_')  
+ "_shared";
-    Set<GwtLocale> localeSet = LocaleUtils.getAllLocales();
-    GwtLocaleImpl[] allLocales = localeSet.toArray(new  
GwtLocaleImpl[localeSet.size()]);
+    Set<GwtLocale> localeSet = localeUtils.getAllLocales();
+    GwtLocaleImpl[] allLocales = localeSet.toArray(
+        new GwtLocaleImpl[localeSet.size()]);
      // sort for deterministic output
      Arrays.sort(allLocales);
      PrintWriter pw = context.tryCreate(logger, packageName,  
superClassName);
@@ -184,10 +187,10 @@
        writer.println("}-*/;");
        writer.commit(logger);
      }
-    GwtLocale locale = LocaleUtils.getCompileLocale();
+    GwtLocale locale = localeUtils.getCompileLocale();
      String className = targetClass.getName().replace('.', '_') + "_"
          + locale.getAsString();
-    Set<GwtLocale> runtimeLocales = LocaleUtils.getRuntimeLocales();
+    Set<GwtLocale> runtimeLocales = localeUtils.getRuntimeLocales();
      if (!runtimeLocales.isEmpty()) {
        className += "_runtimeSelection";
      }
@@ -219,7 +222,6 @@
        writer.println("public DateTimeConstants getDateTimeConstants() {");
        LocalizableGenerator localizableGenerator = new  
LocalizableGenerator();
        // Avoid warnings for trying to create the same type multiple times
-      @SuppressWarnings("hiding")
        GeneratorContext subContext = new CachedGeneratorContext(context);
        generateConstantsLookup(logger, subContext, writer,  
localizableGenerator,
            runtimeLocales, locale,
=======================================
--- /releases/2.0/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java  Thu  
Nov 12 12:19:29 2009
+++ /releases/2.0/user/src/com/google/gwt/i18n/rebind/LocaleUtils.java  Mon  
Nov 16 13:33:45 2009
@@ -25,8 +25,10 @@
  import com.google.gwt.i18n.shared.GwtLocaleFactory;

  import java.util.Collections;
+import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
+import java.util.Map;
  import java.util.Set;
  import java.util.SortedSet;

@@ -34,10 +36,58 @@
   * Utility methods for dealing with locales.
   */
  public class LocaleUtils {
-  // TODO(jat): rewrite to avoid statics
+
+  /**
+   * A key for lookup of computed values in a cache.
+   */
+  private static class CacheKey {
+    private final SelectionProperty localeProperty;
+    private final ConfigurationProperty runtimeLocaleProperty;
+
+    /**
+     * Create a key for cache lookup.
+     *
+     * @param localeProperty "locale" property, must not be null
+     * @param runtimeLocaleProperty "runtime.locales" property, must not  
be null
+     */
+    public CacheKey(SelectionProperty localeProperty,
+        ConfigurationProperty runtimeLocaleProperty) {
+      this.localeProperty = localeProperty;
+      this.runtimeLocaleProperty = runtimeLocaleProperty;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null) {
+        return false;
+      }
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+      CacheKey other = (CacheKey) obj;
+      return localeProperty.equals(other.localeProperty)
+          && runtimeLocaleProperty.equals(other.runtimeLocaleProperty);
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + localeProperty.hashCode();
+      result = prime * result + runtimeLocaleProperty.hashCode();
+      return result;
+    }
+  }

    private static final GwtLocaleFactoryImpl factory = new  
GwtLocaleFactoryImpl();

+  private static final Object cacheLock = new Object[0];
+
+  private static Map<CacheKey, LocaleUtils> cache;
+
    /**
     * The token representing the locale property controlling Localization.
     */
@@ -48,20 +98,123 @@
     */
    private static final String PROP_RUNTIME_LOCALES = "runtime.locales";

-  private static GwtLocale compileLocale;
-
-  private static final Set<GwtLocale> allLocales = new  
HashSet<GwtLocale>();
-
-  private static final Set<GwtLocale> allCompileLocales = new  
HashSet<GwtLocale>();
-
-  private static final Set<GwtLocale> runtimeLocales = new  
HashSet<GwtLocale>();
-
-  public static synchronized void clear() {
-    allCompileLocales.clear();
-    allLocales.clear();
-    compileLocale = null;
-    factory.clear();
-    runtimeLocales.clear();
+  /**
+   * Clear any static state associated with LocaleUtils.
+   */
+  public static synchronized void clear() {
+    factory.clear();
+    synchronized (cacheLock) {
+      cache = null;
+    }
+  }
+
+  /**
+   * Create a new LocaleUtils instance for the given PropertyOracle.   
Returned
+   * instances will be immutable and can be shared across threads.
+   *
+   * @param logger
+   * @param propertyOracle
+   * @return LocaleUtils instance
+   */
+  public static LocaleUtils getInstance(TreeLogger logger,
+      PropertyOracle propertyOracle) {
+    try {
+      SelectionProperty localeProp
+          = propertyOracle.getSelectionProperty(logger, PROP_LOCALE);
+      ConfigurationProperty runtimeLocaleProp
+          = propertyOracle.getConfigurationProperty(PROP_RUNTIME_LOCALES);
+      CacheKey key = new CacheKey(localeProp, runtimeLocaleProp);
+      synchronized (cacheLock) {
+        if (cache == null) {
+          cache = new HashMap<CacheKey, LocaleUtils>();
+        }
+        LocaleUtils localeUtils = cache.get(key);
+        if (localeUtils == null) {
+          localeUtils = createInstance(logger, localeProp,  
runtimeLocaleProp);
+          cache.put(key, localeUtils);
+        }
+        return localeUtils;
+      }
+    } catch (BadPropertyValueException e) {
+      // if we don't have locale properties defined, just return a basic  
one
+      logger.log(TreeLogger.WARN,
+          "Unable to get locale properties, using defaults", e);
+      GwtLocale defaultLocale = factory.fromString("default");
+      Set<GwtLocale> allLocales = new HashSet<GwtLocale>();
+      allLocales.add(defaultLocale);
+      return new LocaleUtils(defaultLocale, allLocales, allLocales,
+          Collections.<GwtLocale>emptySet());
+    }
+  }
+
+  /**
+   * Get a shared GwtLocale factory so instances are cached between all  
uses.
+   *
+   * @return singleton GwtLocaleFactory instance.
+   */
+  public static synchronized GwtLocaleFactory getLocaleFactory() {
+    return factory;
+  }
+
+  private static LocaleUtils createInstance(TreeLogger logger,
+      SelectionProperty localeProp, ConfigurationProperty prop) {
+    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();
+    SortedSet<String> localeValues = localeProp.getPossibleValues();
+
+    GwtLocaleFactory factoryInstance = getLocaleFactory();
+    GwtLocale newCompileLocale = factoryInstance.fromString(localeName);
+    compileLocale = newCompileLocale;
+    for (String localeValue : localeValues) {
+      allCompileLocales.add(factoryInstance.fromString(localeValue));
+    }
+    allLocales.addAll(allCompileLocales);
+
+    List<String> rtLocaleNames = prop.getValues();
+    if (rtLocaleNames != null) {
+      for (String rtLocale : rtLocaleNames) {
+        GwtLocale locale = factoryInstance.fromString(rtLocale);
+        // TODO(jat): remove use of labels
+        existingLocales:
+        for (GwtLocale existing : allCompileLocales) {
+          for (GwtLocale alias : existing.getAliases()) {
+            if (!alias.isDefault() && locale.inheritsFrom(alias)
+                && locale.usesSameScript(alias)) {
+              allLocales.add(locale);
+              break existingLocales;
+            }
+          }
+        }
+        if (!compileLocale.isDefault()
+            && locale.inheritsFrom(compileLocale)
+            && locale.usesSameScript(compileLocale)) {
+          // TODO(jat): don't include runtime locales which also inherit
+          // from a more-specific compile locale than this one
+          runtimeLocales.add(locale);
+        }
+      }
+    }
+    return new LocaleUtils(compileLocale, allLocales, allCompileLocales,
+        runtimeLocales);
+  }
+
+  private final Set<GwtLocale> allCompileLocales;
+
+  private final Set<GwtLocale> allLocales;
+
+  private final GwtLocale compileLocale;
+
+  private final Set<GwtLocale> runtimeLocales;
+
+  private LocaleUtils(GwtLocale compileLocale, Set<GwtLocale> allLocales,
+      Set<GwtLocale> allCompileLocales, Set<GwtLocale> runtimeLocales) {
+    this.compileLocale = compileLocale;
+    this.allLocales = Collections.unmodifiableSet(allLocales);
+    this.allCompileLocales =  
Collections.unmodifiableSet(allCompileLocales);
+    this.runtimeLocales = Collections.unmodifiableSet(runtimeLocales);
    }

    /**
@@ -69,8 +222,8 @@
     *
     * @return unmodifiable set of all compile-time locales
     */
-  public static synchronized Set<GwtLocale> getAllCompileLocales() {
-    return Collections.unmodifiableSet(allCompileLocales);
+  public Set<GwtLocale> getAllCompileLocales() {
+    return allCompileLocales;
    }

    /**
@@ -79,25 +232,16 @@
     *
     * @return unmodifiable set of all locales
     */
-  public static synchronized Set<GwtLocale> getAllLocales() {
-    return Collections.unmodifiableSet(allLocales);
+  public Set<GwtLocale> getAllLocales() {
+    return allLocales;
    }

    /**
     * @return the static compile-time locale for this permutation.
     */
-  public static synchronized GwtLocale getCompileLocale() {
+  public GwtLocale getCompileLocale() {
      return compileLocale;
    }
-
-  /**
-   * Get a shared GwtLocale factory so instances are cached between all  
uses.
-   *
-   * @return singleton GwtLocaleFactory instance.
-   */
-  public static synchronized GwtLocaleFactory getLocaleFactory() {
-    return factory;
-  }

    /**
     * Returns a list of locales which are children of the current  
compile-time
@@ -105,69 +249,7 @@
     *
     * @return unmodifiable list of matching locales
     */
-  public static synchronized Set<GwtLocale> getRuntimeLocales() {
-    return Collections.unmodifiableSet(runtimeLocales);
-  }
-
-  /**
-   * Initialize from properties. Only needs to be called once, before any  
other
-   * calls.
-   *
-   * @param logger
-   * @param propertyOracle
-   */
-  public static synchronized void init(TreeLogger logger, PropertyOracle  
propertyOracle) {
-    try {
-      SelectionProperty localeProp
-          = propertyOracle.getSelectionProperty(logger, PROP_LOCALE);
-      String localeName = localeProp.getCurrentValue();
-      SortedSet<String> localeValues = localeProp.getPossibleValues();
-
-      GwtLocale newCompileLocale = factory.fromString(localeName);
-      if (newCompileLocale.equals(compileLocale)) {
-        return;
-      }
-      compileLocale = newCompileLocale;
-      allLocales.clear();
-      allCompileLocales.clear();
-      runtimeLocales.clear();
-      for (String localeValue : localeValues) {
-        allCompileLocales.add(factory.fromString(localeValue));
-      }
-      allLocales.addAll(allCompileLocales);
-
-      ConfigurationProperty prop
-          = propertyOracle.getConfigurationProperty(PROP_RUNTIME_LOCALES);
-      List<String> rtLocaleNames = prop.getValues();
-      if (rtLocaleNames != null) {
-        for (String rtLocale : rtLocaleNames) {
-          GwtLocale locale = factory.fromString(rtLocale);
-          // TODO(jat): remove use of labels
-          existingLocales:
-          for (GwtLocale existing : allCompileLocales) {
-            for (GwtLocale alias : existing.getAliases()) {
-              if (!alias.isDefault() && locale.inheritsFrom(alias)
-                  && locale.usesSameScript(alias)) {
-                allLocales.add(locale);
-                break existingLocales;
-              }
-            }
-          }
-          if (!compileLocale.isDefault()
-              && locale.inheritsFrom(compileLocale)
-              && locale.usesSameScript(compileLocale)) {
-            // TODO(jat): don't include runtime locales which also inherit
-            // from a more-specific compile locale than this one
-            runtimeLocales.add(locale);
-          }
-        }
-      }
-    } catch (BadPropertyValueException e) {
-      logger.log(TreeLogger.TRACE,
-          "Unable to get locale properties, using defaults", e);
-      compileLocale = factory.fromString("default");
-      allLocales.add(compileLocale);
-      return;
-    }
+  public Set<GwtLocale> getRuntimeLocales() {
+    return runtimeLocales;
    }
  }
=======================================
---  
/releases/2.0/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java     
 
Mon Nov  2 12:44:54 2009
+++  
/releases/2.0/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java     
 
Mon Nov 16 13:33:45 2009
@@ -24,31 +24,38 @@
  import java.util.Map;

  /**
- * Creates server-side GwtLocale instances.
+ * Creates server-side GwtLocale instances.  Thread-safe.
   */
  public class GwtLocaleFactoryImpl implements GwtLocaleFactory {

-  // TODO(jat): remove code duplication here by combining these
    private static boolean isAlpha(String str, int min, int max) {
-    int len = str.length();
-    if (len < min || len > max) {
-      return false;
-    }
-    for (int i = 0; i < len; ++i) {
-      if (!Character.isLetter(str.charAt(i))) {
-        return false;
-      }
-    }
-    return true;
+    return matches(str, min, max, true);
    }

    private static boolean isDigit(String str, int min, int max) {
+    return matches(str, min, max, false);
+  }
+
+  /**
+   * Check if the supplied string matches length and composition  
requirements.
+   *
+   * @param str string to check
+   * @param min minimum length
+   * @param max maximum length
+   * @param lettersNotDigits true if all characters should be letters,  
false if
+   *     all characters should be digits
+   * @return true if the string is of a proper length and contains only the
+   *     specified characters
+   */
+  private static boolean matches(String str, int min, int max,
+      boolean lettersNotDigits) {
      int len = str.length();
      if (len < min || len > max) {
        return false;
      }
      for (int i = 0; i < len; ++i) {
-      if (!Character.isDigit(str.charAt(i))) {
+      if ((lettersNotDigits && !Character.isLetter(str.charAt(i)))
+          || (!lettersNotDigits && !Character.isDigit(str.charAt(i)))) {
          return false;
        }
      }
@@ -63,13 +70,24 @@
          str.substring(1).toLowerCase(Locale.ENGLISH);
    }

+  private final Object instanceCacheLock = new Object[0];
+
    // Locales are stored pointing at themselves. A new instance is created,
    // which is pretty cheap, then looked up here. If it exists, the old
    // one is used instead to preserved cached data structures.
-  private Map<GwtLocaleImpl, GwtLocaleImpl> instanceCache = new  
HashMap<GwtLocaleImpl, GwtLocaleImpl>();
-
+  private Map<GwtLocaleImpl, GwtLocaleImpl> instanceCache
+      = new HashMap<GwtLocaleImpl, GwtLocaleImpl>();
+
+  /**
+   * Clear an embedded cache of instances when they are no longer needed.
+   * <p>
+   * Note that GwtLocale instances constructed after this is called will  
not
+   * maintain identity with instances constructed before this call.
+   */
    public void clear() {
-    instanceCache.clear();
+    synchronized (instanceCacheLock) {
+      instanceCache.clear();
+    }
    }

    public GwtLocale fromComponents(String language, String script,
@@ -100,10 +118,12 @@
      }
      GwtLocaleImpl locale = new GwtLocaleImpl(this, language, region,  
script,
          variant);
-    if (instanceCache.containsKey(locale)) {
-      return instanceCache.get(locale);
-    }
-    instanceCache.put(locale, locale);
+    synchronized (instanceCacheLock) {
+      if (instanceCache.containsKey(locale)) {
+        return instanceCache.get(locale);
+      }
+      instanceCache.put(locale, locale);
+    }
      return locale;
    }

@@ -185,5 +205,4 @@
    public GwtLocale getDefault() {
      return fromComponents(null, null, null, null);
    }
-
-}
+}
=======================================
--- /releases/2.0/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java        
 
Mon Nov  2 12:44:54 2009
+++ /releases/2.0/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java        
 
Mon Nov 16 13:33:45 2009
@@ -40,9 +40,9 @@
     * Maps deprecated language codes to the canonical code.  Strings are  
always
     * in pairs, with the first being the canonical code and the second being
     * a deprecated code which maps to it.
-   *
+   * <p>
     * Source: http://www.loc.gov/standards/iso639-2/php/code_changes.php
-   *
+   * <p>
     * TODO: consider building maps if this list grows much.
     */
    private static final String[] deprecatedLanguages = new String[] {
@@ -62,9 +62,9 @@
     * cannot be done automatically (such as cs -> rs/me) -- perhaps we could
     * have a way of flagging region codes which are no longer valid and  
allow
     * an appropriate warning message.
-   *
+   * <p>
     * Source: http://en.wikipedia.org/wiki/ISO_3166-1
-   *
+   * <p>
     * TODO: consider building maps if this list grows much.
     */
    private static final String[] deprecatedRegions = new String[] {
@@ -233,9 +233,13 @@

    private final String variant;

-  private ArrayList<GwtLocale> cachedSearchList;
-
-  private ArrayList<GwtLocale> cachedAliases;
+  private final Object cacheLock = new Object[0];
+
+  // protected by cacheLock
+  private List<GwtLocale> cachedSearchList;
+
+  // protected by cacheLock
+  private List<GwtLocale> cachedAliases;

    /**
     * Must only be called from a factory to preserve instance caching.
@@ -280,40 +284,44 @@

    public List<GwtLocale> getAliases() {
      // TODO(jat): more locale aliases? better way to encode them?
-    if (cachedAliases == null) {
-      cachedAliases = new ArrayList<GwtLocale>();
-      GwtLocale canonicalForm = getCanonicalForm();
-      Set<GwtLocale> seen = new HashSet<GwtLocale>();
-      cachedAliases.add(canonicalForm);
-      ArrayList<GwtLocale> nextGroup = new ArrayList<GwtLocale>();
-      nextGroup.add(this);
-      // Account for default script
-      String defaultScript =  
DefaultLanguageScripts.getDefaultScript(language);
-      if (defaultScript != null) {
-        if (script == null) {
-          nextGroup.add(factory.fromComponents(language, defaultScript,  
region,
-              variant));
-        } else if (script.equals(defaultScript)) {
-          nextGroup.add(factory.fromComponents(language, null, region,  
variant));
-        }
-      }
-      while (!nextGroup.isEmpty()) {
-        List<GwtLocale> thisGroup = nextGroup;
-        nextGroup = new ArrayList<GwtLocale>();
-        for (GwtLocale locale : thisGroup) {
-          if (seen.contains(locale)) {
-            continue;
-          }
-          seen.add(locale);
-          if (!locale.equals(canonicalForm)) {
-            cachedAliases.add(locale);
-          }
-          addDeprecatedPairs(factory, locale, nextGroup);
-          addSpecialAliases(factory, locale, nextGroup);
-        }
-      }
-    }
-    return Collections.unmodifiableList(cachedAliases);
+    synchronized (cacheLock) {
+      if (cachedAliases == null) {
+        cachedAliases = new ArrayList<GwtLocale>();
+        GwtLocale canonicalForm = getCanonicalForm();
+        Set<GwtLocale> seen = new HashSet<GwtLocale>();
+        cachedAliases.add(canonicalForm);
+        ArrayList<GwtLocale> nextGroup = new ArrayList<GwtLocale>();
+        nextGroup.add(this);
+        // Account for default script
+        String defaultScript =  
DefaultLanguageScripts.getDefaultScript(language);
+        if (defaultScript != null) {
+          if (script == null) {
+            nextGroup.add(factory.fromComponents(language, defaultScript,
+                region, variant));
+          } else if (script.equals(defaultScript)) {
+            nextGroup.add(factory.fromComponents(language, null, region,
+                variant));
+          }
+        }
+        while (!nextGroup.isEmpty()) {
+          List<GwtLocale> thisGroup = nextGroup;
+          nextGroup = new ArrayList<GwtLocale>();
+          for (GwtLocale locale : thisGroup) {
+            if (seen.contains(locale)) {
+              continue;
+            }
+            seen.add(locale);
+            if (!locale.equals(canonicalForm)) {
+              cachedAliases.add(locale);
+            }
+            addDeprecatedPairs(factory, locale, nextGroup);
+            addSpecialAliases(factory, locale, nextGroup);
+          }
+        }
+        cachedAliases = Collections.unmodifiableList(cachedAliases);
+      }
+      return cachedAliases;
+    }
    }

    public String getAsString() {
@@ -408,42 +416,45 @@

    public List<GwtLocale> getCompleteSearchList() {
      // TODO(jat): get zh_Hant to come before zh in search list for zh_TW
-    if (cachedSearchList == null) {
-      cachedSearchList = new ArrayList<GwtLocale>();
-      Set<GwtLocale> seen = new HashSet<GwtLocale>();
-      List<GwtLocale> thisGroup = new  
ArrayList<GwtLocale>(this.getAliases());
-      seen.addAll(thisGroup);
-      GwtLocale defLocale = factory.getDefault();
-      seen.add(defLocale);
-      while (!thisGroup.isEmpty()) {
-        cachedSearchList.addAll(thisGroup);
-        List<GwtLocale> nextGroup = new ArrayList<GwtLocale>();
-        for (GwtLocale locale : thisGroup) {
-          List<GwtLocale> work = new  
ArrayList<GwtLocale>(locale.getAliases());
-          work.removeAll(seen);
-          nextGroup.addAll(work);
-          seen.addAll(work);
-          work.clear();
-          if (locale.getRegion() != null) {
-            addImmediateParentRegions(factory, locale, work);
-          } else if (locale.getVariant() != null) {
-            work.add(factory.fromComponents(locale.getLanguage(),
-                locale.getScript(), null, null));
-          } else if (locale.getScript() != null) {
-            work.add(factory.fromComponents(locale.getLanguage(), null,  
null,
-                null));
-          }
-          work.removeAll(seen);
-          nextGroup.addAll(work);
-          seen.addAll(work);
-        }
-        thisGroup = nextGroup;
-      }
-      if (!isDefault()) {
-        cachedSearchList.add(defLocale);
-      }
-    }
-    return cachedSearchList;
+    synchronized (cacheLock) {
+      if (cachedSearchList == null) {
+        cachedSearchList = new ArrayList<GwtLocale>();
+        Set<GwtLocale> seen = new HashSet<GwtLocale>();
+        List<GwtLocale> thisGroup = new  
ArrayList<GwtLocale>(this.getAliases());
+        seen.addAll(thisGroup);
+        GwtLocale defLocale = factory.getDefault();
+        seen.add(defLocale);
+        while (!thisGroup.isEmpty()) {
+          cachedSearchList.addAll(thisGroup);
+          List<GwtLocale> nextGroup = new ArrayList<GwtLocale>();
+          for (GwtLocale locale : thisGroup) {
+            List<GwtLocale> work = new  
ArrayList<GwtLocale>(locale.getAliases());
+            work.removeAll(seen);
+            nextGroup.addAll(work);
+            seen.addAll(work);
+            work.clear();
+            if (locale.getRegion() != null) {
+              addImmediateParentRegions(factory, locale, work);
+            } else if (locale.getVariant() != null) {
+              work.add(factory.fromComponents(locale.getLanguage(),
+                  locale.getScript(), null, null));
+            } else if (locale.getScript() != null) {
+              work.add(factory.fromComponents(locale.getLanguage(), null,  
null,
+                  null));
+            }
+            work.removeAll(seen);
+            nextGroup.addAll(work);
+            seen.addAll(work);
+          }
+          thisGroup = nextGroup;
+        }
+        if (!isDefault()) {
+          cachedSearchList.add(defLocale);
+        }
+        cachedSearchList = Collections.unmodifiableList(cachedSearchList);
+      }
+      return cachedSearchList;
+    }
    }

    /**

-- 
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to