Change CurrencyListGenerator to take advantage of inheritance in the generated
classes, to minimize the amount of data seen by the compiler. Also, add
additional runtime locales tests.
Patch by: jat, scottb
Review by: scottb, jat
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5174 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/i18n/client/impl/CurrencyList.java b/user/src/com/google/gwt/i18n/client/impl/CurrencyList.java
index 25af062..9be96b7 100644
--- a/user/src/com/google/gwt/i18n/client/impl/CurrencyList.java
+++ b/user/src/com/google/gwt/i18n/client/impl/CurrencyList.java
@@ -133,7 +133,7 @@
loadNamesMap();
}
}
-
+
/**
* Directly reference an entry in the currency map JSO.
*
@@ -141,14 +141,14 @@
* @return currency data
*/
protected final native CurrencyData getEntry(String code) /*-{
- return this.@com.google.gwt.i18n.client.impl.CurrencyList::dataMap[':' + code];
+ return this.@com.google.gwt.i18n.client.impl.CurrencyList::dataMap[code];
}-*/;
/**
* Directly reference an entry in the currency names map JSO.
*
* @param code ISO4217 currency code
- * @return currency name
+ * @return currency name, or the currency code if not known
*/
protected final native String getNamesEntry(String code) /*-{
return this.@com.google.gwt.i18n.client.impl.CurrencyList::namesMap[code] || code;
@@ -161,10 +161,10 @@
*/
protected native void loadCurrencyMap() /*-{
this.@com.google.gwt.i18n.client.impl.CurrencyList::dataMap = {
- ":USD": [ "USD", "$", 2 ],
- ":EUR": [ "EUR", "€", 2 ],
- ":GBP": [ "GBP", "UK£", 2 ],
- ":JPY": [ "JPY", "¥", 0 ],
+ "USD": [ "USD", "$", 2 ],
+ "EUR": [ "EUR", "€", 2 ],
+ "GBP": [ "GBP", "UK£", 2 ],
+ "JPY": [ "JPY", "¥", 0 ],
};
}-*/;
@@ -183,13 +183,45 @@
}-*/;
/**
+ * Add all entries in {@code override} to the currency data map, replacing
+ * any existing entries. This is used by subclasses that need to slightly
+ * alter the data used by the parent locale.
+ *
+ * @param override JS object with currency code -> CurrencyData pairs
+ */
+ protected final native void overrideCurrencyMap(JavaScriptObject override) /*-{
+ var map = this.@com.google.gwt.i18n.client.impl.CurrencyList::dataMap;
+ for (var key in override) {
+ if (override.hasOwnProperty(key)) {
+ map[key] = override[key];
+ }
+ }
+ }-*/;
+
+ /**
+ * Add all entries in {@code override} to the currency name map, replacing
+ * any existing entries. This is used by subclasses that need to slightly
+ * alter the data used by the parent locale.
+ *
+ * @param override JS object with currency code -> name pairs
+ */
+ protected final native void overrideNamesMap(JavaScriptObject override) /*-{
+ var map = this.@com.google.gwt.i18n.client.impl.CurrencyList::namesMap;
+ for (var key in override) {
+ if (override.hasOwnProperty(key)) {
+ map[key] = override[key];
+ }
+ }
+ }-*/;
+
+ /**
* Add currency codes contained in the map to an ArrayList.
*/
private native void loadCurrencyKeys(ArrayList<String> keys) /*-{
var map = this.@com.google.gwt.i18n.client.impl.CurrencyList::dataMap;
for (var key in map) {
- if (key.charCodeAt(0) == 58) {
- keys.@java.util.ArrayList::add(Ljava/lang/Object;)(key.substring(1));
+ if (map.hasOwnProperty(key)) {
+ keys.@java.util.ArrayList::add(Ljava/lang/Object;)(key);
}
}
}-*/;
diff --git a/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java b/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java
index 64fc211..3b61416 100644
--- a/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java
+++ b/user/src/com/google/gwt/i18n/rebind/CurrencyListGenerator.java
@@ -36,6 +36,7 @@
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -43,6 +44,7 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
+import java.util.regex.Pattern;
/**
* Generator used to generate a localized version of CurrencyList, which
@@ -51,9 +53,160 @@
*/
public class CurrencyListGenerator extends Generator {
- private static final String CURRENCY_DATA = CurrencyData.class.getCanonicalName();
+ /**
+ * Immutable collection of data about a currency in a locale, built from the
+ * CurrencyData and CurrencyExtra properties files.
+ */
+ private static class CurrencyInfo {
- private static final String CURRENCY_LIST = CurrencyList.class.getCanonicalName();
+ private static final Pattern SPLIT_VERTICALBAR = Pattern.compile("\\|");
+
+ private final String code;
+
+ private final String displayName;
+
+ private final int flags;
+
+ private final boolean obsolete;
+
+ private final String portableSymbol;
+
+ private final String symbol;
+
+ /**
+ * Create an instance.
+ *
+ * currencyData format:
+ *
+ * <pre>
+ * display name|symbol|decimal digits|not-used-flag
+ * </pre>
+ *
+ * <ul>
+ * <li>If a symbol is not supplied, the currency code will be used
+ * <li>If # of decimal digits is omitted, 2 is used
+ * <li>If a currency is not generally used, not-used-flag=1
+ * <li>Trailing empty fields can be omitted
+ * <li>If null, use currencyCode as the display name
+ * </ul>
+ *
+ * extraData format:
+ *
+ * <pre>
+ * portable symbol|flags|currency symbol override
+ * flags are space separated list of:
+ * At most one of the following:
+ * SymPrefix The currency symbol goes before the number,
+ * regardless of the normal position for this locale.
+ * SymSuffix The currency symbol goes after the number,
+ * regardless of the normal position for this locale.
+ *
+ * At most one of the following:
+ * ForceSpace Always add a space between the currency symbol
+ * and the number.
+ * ForceNoSpace Never add a space between the currency symbol
+ * and the number.
+ * </pre>
+ *
+ * @param currencyCode ISO4217 currency code
+ * @param currencyData entry from a CurrencyData properties file
+ * @param extraData entry from a CurrencyExtra properties file
+ * @throws NumberFormatException
+ */
+ public CurrencyInfo(String currencyCode, String currencyData,
+ String extraData) throws NumberFormatException {
+ code = currencyCode;
+ if (currencyData == null) {
+ currencyData = currencyCode;
+ }
+ String[] currencySplit = SPLIT_VERTICALBAR.split(currencyData);
+ String currencyDisplay = currencySplit[0];
+ String currencySymbol = null;
+ if (currencySplit.length > 1 && currencySplit[1].length() > 0) {
+ currencySymbol = currencySplit[1];
+ }
+ int currencyFractionDigits = 2;
+ if (currencySplit.length > 2 && currencySplit[2].length() > 0) {
+ currencyFractionDigits = Integer.valueOf(currencySplit[2]);
+ }
+ boolean currencyObsolete = false;
+ if (currencySplit.length > 3 && currencySplit[3].length() > 0) {
+ currencyObsolete = Integer.valueOf(currencySplit[3]) != 0;
+ }
+ int currencyFlags = currencyFractionDigits;
+ String currencyPortableSymbol = "";
+ if (extraData != null) {
+ // CurrencyExtra contains up to 3 fields separated by |
+ // 0 - portable currency symbol
+ // 1 - space-separated flags regarding currency symbol
+ // positioning/spacing
+ // 2 - override of CLDR-derived currency symbol
+ String[] extraSplit = SPLIT_VERTICALBAR.split(extraData);
+ currencyPortableSymbol = extraSplit[0];
+ if (extraSplit.length > 1) {
+ if (extraSplit[1].contains("SymPrefix")) {
+ currencyFlags |= CurrencyData.POS_FIXED_FLAG;
+ } else if (extraSplit[1].contains("SymSuffix")) {
+ currencyFlags |= CurrencyData.POS_FIXED_FLAG
+ | CurrencyData.POS_SUFFIX_FLAG;
+ }
+ if (extraSplit[1].contains("ForceSpace")) {
+ currencyFlags |= CurrencyData.SPACING_FIXED_FLAG
+ | CurrencyData.SPACE_FORCED_FLAG;
+ } else if (extraSplit[1].contains("ForceNoSpace")) {
+ currencyFlags |= CurrencyData.SPACING_FIXED_FLAG;
+ }
+ }
+ // If a non-empty override is supplied, use it for the currency
+ // symbol.
+ if (extraSplit.length > 2 && extraSplit[2].length() > 0) {
+ currencySymbol = extraSplit[2];
+ }
+ // If we don't have a currency symbol yet, use the portable symbol if
+ // supplied.
+ if (currencySymbol == null && currencyPortableSymbol.length() > 0) {
+ currencySymbol = currencyPortableSymbol;
+ }
+ }
+ // If all else fails, use the currency code as the symbol.
+ if (currencySymbol == null) {
+ currencySymbol = currencyCode;
+ }
+ displayName = currencyDisplay;
+ symbol = currencySymbol;
+ flags = currencyFlags;
+ portableSymbol = currencyPortableSymbol;
+ obsolete = currencyObsolete;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ public String getJson() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("[ \"").append(quote(code)).append("\", \"");
+ buf.append(quote(symbol)).append("\", ").append(flags);
+ if (portableSymbol.length() > 0) {
+ buf.append(", \"").append(quote(portableSymbol)).append('\"');
+ }
+ return buf.append(']').toString();
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+
+ public boolean isObsolete() {
+ return obsolete;
+ }
+ }
+
+ private static final String CURRENCY_DATA = CurrencyData.class.getCanonicalName();
/**
* Prefix for properties files containing CLDR-derived currency data for each
@@ -67,6 +220,8 @@
*/
private static final String CURRENCY_EXTRA_PREFIX = "com/google/gwt/i18n/client/constants/CurrencyExtra";
+ private static final String CURRENCY_LIST = CurrencyList.class.getCanonicalName();
+
/**
* Prefix for properties files containing number formatting constants for each
* locale. We use this only to get the default currency for our current
@@ -75,6 +230,16 @@
private static final String NUMBER_CONSTANTS_PREFIX = "com/google/gwt/i18n/client/constants/NumberConstantsImpl";
/**
+ * Backslash-escape any double quotes in the supplied string.
+ *
+ * @param str string to quote
+ * @return string with double quotes backslash-escaped.
+ */
+ private static String quote(String str) {
+ return str.replace("\"", "\\\"");
+ }
+
+ /**
* Generate an implementation for the given type.
*
* @param logger error logger
@@ -102,179 +267,147 @@
throw new UnableToCompleteException();
}
if (runtimeLocales.isEmpty()) {
- return generateOne(logger, context, targetClass, locale);
+ return generateLocaleTree(logger, context, targetClass, locale);
}
CachedGeneratorContext cachedContext = new CachedGeneratorContext(context);
return generateRuntimeSelection(logger, cachedContext, targetClass, locale,
runtimeLocales);
}
-
- private String generateOne(TreeLogger logger, GeneratorContext context,
- JClassType targetClass, GwtLocale locale)
- throws UnableToCompleteException {
+
+ /**
+ * Generate an implementation class for the requested locale, including all
+ * parent classes along the inheritance chain. The data will be kept at the
+ * location in the inheritance chain where it was defined in properties files.
+ *
+ * @param logger
+ * @param context
+ * @param targetClass
+ * @param locale
+ * @return generated class name for the requested locale
+ */
+ private String generateLocaleTree(TreeLogger logger,
+ GeneratorContext context, JClassType targetClass, GwtLocale locale) {
+ String superClassName = CURRENCY_LIST;
+ List<GwtLocale> searchList = locale.getCompleteSearchList();
+
+ /**
+ * Map of currency code -> CurrencyInfo for that code.
+ */
+ Map<String, CurrencyInfo> allCurrencyData = new HashMap<String, CurrencyInfo>();
+
+ LocalizedProperties currencyExtra = null;
+ /*
+ * The searchList is guaranteed to be ordered such that subclasses always
+ * precede superclasses. Therefore, we iterate backwards to ensure that
+ * superclasses are always generated first.
+ */
+ String lastDefaultCurrencyCode = null;
+ for (int i = searchList.size(); i-- > 0;) {
+ GwtLocale search = searchList.get(i);
+ LocalizedProperties newExtra = getProperties(CURRENCY_EXTRA_PREFIX,
+ search);
+ if (newExtra != null) {
+ currencyExtra = newExtra;
+ }
+ Map<String, String> currencyData = getCurrencyData(search);
+ Set<String> keySet = currencyData.keySet();
+ String[] currencies = new String[keySet.size()];
+ keySet.toArray(currencies);
+ Arrays.sort(currencies);
+
+ // Go ahead and populate the data map.
+ for (String currencyCode : currencies) {
+ String extraData = currencyExtra == null ? null
+ : currencyExtra.getProperty(currencyCode);
+ allCurrencyData.put(currencyCode, new CurrencyInfo(currencyCode,
+ currencyData.get(currencyCode), extraData));
+ }
+
+ String defCurrencyCode = getDefaultCurrency(search);
+ // If this locale specifies a particular locale, or the one that is
+ // inherited has been changed in this locale, re-specify the default
+ // currency so the method will be generated.
+ if (defCurrencyCode == null && keySet.contains(lastDefaultCurrencyCode)) {
+ defCurrencyCode = lastDefaultCurrencyCode;
+ }
+ if (!currencyData.isEmpty() || defCurrencyCode != null) {
+ String newClass = generateOneLocale(logger, context, targetClass,
+ search, superClassName, currencies, allCurrencyData,
+ defCurrencyCode);
+ superClassName = newClass;
+ lastDefaultCurrencyCode = defCurrencyCode;
+ }
+ }
+ return superClassName;
+ }
+
+ /**
+ * Generate the implementation for a single locale, overriding from its parent
+ * only data that has changed in this locale.
+ *
+ * @param logger
+ * @param context
+ * @param targetClass
+ * @param locale
+ * @param superClassName
+ * @param currencies the set of currencies defined in this locale
+ * @param allCurrencyData map of currency code -> unparsed CurrencyInfo for
+ * that code
+ * @param defCurrencyCode default currency code for this locale
+ * @return fully-qualified class name generated
+ */
+ private String generateOneLocale(TreeLogger logger, GeneratorContext context,
+ JClassType targetClass, GwtLocale locale, String superClassName,
+ String[] currencies, Map<String, CurrencyInfo> allCurrencyData,
+ String defCurrencyCode) {
+
String packageName = targetClass.getPackage().getName();
String className = targetClass.getName().replace('.', '_') + "_"
- + locale.getAsString();
+ + locale.getCanonicalForm().getAsString();
PrintWriter pw = context.tryCreate(logger, packageName, className);
if (pw != null) {
ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(
packageName, className);
- factory.setSuperclass(targetClass.getQualifiedSourceName());
+ factory.setSuperclass(superClassName);
factory.addImport(CURRENCY_LIST);
factory.addImport(CURRENCY_DATA);
SourceWriter writer = factory.createSourceWriter(context, pw);
-
- // Load property files for this locale, handling inheritance properly.
- GwtLocale[] currencyLocale = new GwtLocale[1];
- LocalizedProperties currencyData = readProperties(logger,
- CURRENCY_DATA_PREFIX, locale, currencyLocale);
- GwtLocale[] extraLocale = new GwtLocale[1];
- LocalizedProperties currencyExtra = readProperties(logger,
- CURRENCY_EXTRA_PREFIX, locale, extraLocale);
- GwtLocale[] numberLocale = new GwtLocale[1];
- LocalizedProperties numberConstants = readProperties(logger,
- NUMBER_CONSTANTS_PREFIX, locale, numberLocale);
-
- // Get default currency code, set defaults in case it isn't found.
- String defCurrencyCode = numberConstants.getProperty("defCurrencyCode");
- if (defCurrencyCode == null) {
- defCurrencyCode = "USD";
+ if (currencies.length > 0) {
+ writeCurrencyMethod(className, writer, currencies, allCurrencyData);
+ writeNamesMethod(className, writer, currencies, allCurrencyData);
}
-
- // Sort for deterministic output.
- Set<?> keySet = currencyData.getPropertyMap().keySet();
- String[] currencies = new String[keySet.size()];
- keySet.toArray(currencies);
- Arrays.sort(currencies);
- Map<String, String> nameMap = new HashMap<String, String>();
-
- writer.println("@Override");
- writer.println("protected native void loadCurrencyMap() /*-{");
- writer.indent();
- writer.println("this.@com.google.gwt.i18n.client.impl.CurrencyList::dataMap = {");
- writer.indent();
- String defCurrencyObject = "[ \"" + quote(defCurrencyCode) + "\", \""
- + quote(defCurrencyCode) + "\", 2 ]";
- for (String currencyCode : currencies) {
- String currencyEntry = currencyData.getProperty(currencyCode);
- String[] currencySplit = currencyEntry.split("\\|");
- String currencyDisplay = currencySplit[0];
- String currencySymbol = null;
- if (currencySplit.length > 1 && currencySplit[1].length() > 0) {
- currencySymbol = currencySplit[1];
+ if (defCurrencyCode != null) {
+ CurrencyInfo currencyInfo = allCurrencyData.get(defCurrencyCode);
+ if (currencyInfo == null) {
+ // Synthesize a null info if the specified default wasn't found.
+ currencyInfo = new CurrencyInfo(defCurrencyCode, null, null);
+ allCurrencyData.put(defCurrencyCode, currencyInfo);
}
- int currencyFractionDigits = 2;
- if (currencySplit.length > 2 && currencySplit[2].length() > 0) {
- try {
- currencyFractionDigits = Integer.valueOf(currencySplit[2]);
- } catch (NumberFormatException e) {
- // Ignore bad values
- logger.log(TreeLogger.WARN, "Parse of \"" + currencySplit[2]
- + "\" failed", e);
- }
- }
- boolean currencyObsolete = false;
- if (currencySplit.length > 3 && currencySplit[3].length() > 0) {
- try {
- currencyObsolete = Integer.valueOf(currencySplit[3]) != 0;
- } catch (NumberFormatException e) {
- // Ignore bad values
- logger.log(TreeLogger.WARN, "Parse of \"" + currencySplit[3]
- + "\" failed", e);
- }
- }
- int currencyFlags = currencyFractionDigits;
- String extraData = currencyExtra.getProperty(currencyCode);
- String portableSymbol = "";
- if (extraData != null) {
- // CurrencyExtra contains up to 3 fields separated by |
- // 0 - portable currency symbol
- // 1 - space-separated flags regarding currency symbol
- // positioning/spacing
- // 2 - override of CLDR-derived currency symbol
- String[] extraSplit = extraData.split("\\|");
- portableSymbol = extraSplit[0];
- if (extraSplit.length > 1) {
- if (extraSplit[1].contains("SymPrefix")) {
- currencyFlags |= CurrencyData.POS_FIXED_FLAG;
- } else if (extraSplit[1].contains("SymSuffix")) {
- currencyFlags |= CurrencyData.POS_FIXED_FLAG
- | CurrencyData.POS_SUFFIX_FLAG;
- }
- if (extraSplit[1].contains("ForceSpace")) {
- currencyFlags |= CurrencyData.SPACING_FIXED_FLAG
- | CurrencyData.SPACE_FORCED_FLAG;
- } else if (extraSplit[1].contains("ForceNoSpace")) {
- currencyFlags |= CurrencyData.SPACING_FIXED_FLAG;
- }
- }
- // If a non-empty override is supplied, use it for the currency
- // symbol.
- if (extraSplit.length > 2 && extraSplit[2].length() > 0) {
- currencySymbol = extraSplit[2];
- }
- // If we don't have a currency symbol yet, use the portable symbol if
- // supplied.
- if (currencySymbol == null && portableSymbol.length() > 0) {
- currencySymbol = portableSymbol;
- }
- }
- // If all else fails, use the currency code as the symbol.
- if (currencySymbol == null) {
- currencySymbol = currencyCode;
- }
- String currencyObject = "[ \"" + quote(currencyCode) + "\", \""
- + quote(currencySymbol) + "\", " + currencyFlags;
- if (portableSymbol.length() > 0) {
- currencyObject += ", \"" + quote(portableSymbol) + "\"";
- }
- currencyObject += "]";
- if (!currencyObsolete) {
- nameMap.put(currencyCode, currencyDisplay);
- writer.println("// " + currencyDisplay);
- writer.println("\":" + quote(currencyCode) + "\": " + currencyObject
- + ",");
- }
- if (currencyCode.equals(defCurrencyCode)) {
- defCurrencyObject = currencyObject;
- }
+ writer.println();
+ writer.println("@Override");
+ writer.println("public native CurrencyData getDefault() /*-{");
+ writer.println(" return " + currencyInfo.getJson() + ";");
+ writer.println("}-*/;");
}
- writer.outdent();
- writer.println("};");
- writer.outdent();
- writer.println("}-*/;");
- writer.println();
- writer.println("@Override");
- writer.println("protected native void loadNamesMap() /*-{");
- writer.indent();
- writer.println("this.@com.google.gwt.i18n.client.impl.CurrencyList::namesMap = {");
- writer.indent();
- for (String currencyCode : currencies) {
- String displayName = nameMap.get(currencyCode);
- if (displayName != null && !currencyCode.equals(displayName)) {
- writer.println("\"" + quote(currencyCode) + "\": \""
- + quote(displayName) + "\",");
- }
- }
- writer.outdent();
- writer.println("};");
- writer.outdent();
- writer.println("}-*/;");
- writer.println();
- writer.println("@Override");
- writer.println("public native CurrencyData getDefault() /*-{");
- writer.println(" return " + defCurrencyObject + ";");
- writer.println("}-*/;");
writer.commit(logger);
}
return packageName + "." + className;
}
-
+ /**
+ * Generate a class which can select between alternate implementations at
+ * runtime based on the runtime locale.
+ *
+ * @param logger TreeLogger instance for log messages
+ * @param context GeneratorContext for generating source files
+ * @param targetClass class to generate
+ * @param compileLocale the compile-time locale we are generating for
+ * @param locales set of all locales to generate
+ * @return fully-qualified class name that was generated
+ */
private String generateRuntimeSelection(TreeLogger logger,
GeneratorContext context, JClassType targetClass,
- GwtLocale compileLocale, Set<GwtLocale> locales)
- throws UnableToCompleteException {
+ GwtLocale compileLocale, Set<GwtLocale> locales) {
String packageName = targetClass.getPackage().getName();
String className = targetClass.getName().replace('.', '_') + "_"
+ compileLocale.getAsString() + "_runtimeSelection";
@@ -315,8 +448,7 @@
writer.println(" return;");
writer.println("}");
boolean fetchedLocale = false;
- Map<String, Set<GwtLocale>> localeMap = new TreeMap<String,
- Set<GwtLocale>>();
+ Map<String, Set<GwtLocale>> localeMap = new TreeMap<String, Set<GwtLocale>>();
String compileLocaleClass = processChildLocale(logger, context,
targetClass, localeMap, compileLocale);
if (compileLocaleClass == null) {
@@ -346,8 +478,7 @@
writer.println();
writer.print(" || ");
}
- writer.print("\"" + locale.toString()
- + "\".equals(runtimeLocale)");
+ writer.print("\"" + locale.toString() + "\".equals(runtimeLocale)");
}
writer.println(") {");
writer.println(" instance = new " + generatedClass + "();");
@@ -362,11 +493,108 @@
return packageName + "." + className;
}
- private String processChildLocale(TreeLogger logger, GeneratorContext context,
- JClassType targetClass, Map<String, Set<GwtLocale>> localeMap,
- GwtLocale locale) throws UnableToCompleteException {
- String generatedClass = generateOne(logger, context,
- targetClass, locale);
+ /**
+ * Return a map of currency data for the requested locale, or null if there is
+ * not one (not that inheritance is not handled here).
+ *
+ * The keys are ISO4217 currency codes. The format of the map values is:
+ *
+ * <pre>
+ * display name|symbol|decimal digits|not-used-flag
+ * </pre>
+ *
+ * If a symbol is not supplied, the currency code will be used If # of decimal
+ * digits is omitted, 2 is used If a currency is not generally used,
+ * not-used-flag=1 Trailing empty fields can be omitted
+ *
+ * @param locale
+ * @return currency data map
+ */
+ @SuppressWarnings("unchecked")
+ private Map<String, String> getCurrencyData(GwtLocale locale) {
+ LocalizedProperties currencyData = getProperties(CURRENCY_DATA_PREFIX,
+ locale);
+ if (currencyData == null) {
+ return Collections.emptyMap();
+ }
+ return currencyData.getPropertyMap();
+ }
+
+ /**
+ * Returns the default currency code for the requested locale.
+ *
+ * @param locale
+ * @return ISO4217 currency code
+ */
+ private String getDefaultCurrency(GwtLocale locale) {
+ String defCurrencyCode = null;
+ LocalizedProperties numberConstants = getProperties(
+ NUMBER_CONSTANTS_PREFIX, locale);
+ if (numberConstants != null) {
+ defCurrencyCode = numberConstants.getProperty("defCurrencyCode");
+ }
+ if (defCurrencyCode == null && locale.isDefault()) {
+ defCurrencyCode = "USD";
+ }
+ return defCurrencyCode;
+ }
+
+ /**
+ * Load a properties file for a given locale. Note that locale inheritance is
+ * the responsibility of the caller.
+ *
+ * @param prefix classpath prefix of properties file
+ * @param locale locale to load
+ * @return LocalizedProperties instance containing properties file or null if
+ * not found.
+ */
+ private LocalizedProperties getProperties(String prefix, GwtLocale locale) {
+ String propFile = prefix;
+ if (!locale.isDefault()) {
+ propFile += "_" + locale.getAsString();
+ }
+ propFile += ".properties";
+ InputStream str = null;
+ ClassLoader classLoader = getClass().getClassLoader();
+ LocalizedProperties props = new LocalizedProperties();
+ try {
+ str = classLoader.getResourceAsStream(propFile);
+ if (str != null) {
+ props.load(str, "UTF-8");
+ return props;
+ }
+ } catch (UnsupportedEncodingException e) {
+ // UTF-8 should always be defined
+ return null;
+ } catch (IOException e) {
+ return null;
+ } finally {
+ if (str != null) {
+ try {
+ str.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generate an implementation for a runtime locale, to be referenced from the
+ * generated runtime selection code.
+ *
+ * @param logger
+ * @param context
+ * @param targetClass
+ * @param localeMap
+ * @param locale
+ * @return class name of the generated class, or null if failed
+ */
+ private String processChildLocale(TreeLogger logger,
+ GeneratorContext context, JClassType targetClass,
+ Map<String, Set<GwtLocale>> localeMap, GwtLocale locale) {
+ String generatedClass = generateLocaleTree(logger, context, targetClass,
+ locale);
if (generatedClass == null) {
logger.log(TreeLogger.ERROR, "Failed to generate "
+ targetClass.getQualifiedSourceName() + " in locale "
@@ -384,84 +612,105 @@
}
/**
- * Backslash-escape any double quotes in the supplied string.
+ * Writes a loadCurrencyMap method for the current locale, based on its
+ * currency data and its superclass (if any). As currencies are included in
+ * this method, their names are added to {@code nameMap} for later use.
*
- * @param str string to quote
- * @return string with double quotes backslash-escaped.
+ * If no new currency data is added for this locale over its superclass, the
+ * method is omitted entirely.
+ *
+ * @param allCurrencyData map of currency codes to currency data for the
+ * current locale, including all inherited currencies data
+ * @param className name of the class we are generating
+ * @param writer SourceWriter instance to use for writing the class
+ * @param currencies array of valid currency names in the order they should be
+ * listed
*/
- private String quote(String str) {
- return str.replace("\"", "\\\"");
+ private void writeCurrencyMethod(String className, SourceWriter writer,
+ String[] currencies, Map<String, CurrencyInfo> allCurrencyData) {
+ boolean needHeader = true;
+ for (String currencyCode : currencies) {
+ CurrencyInfo currencyInfo = allCurrencyData.get(currencyCode);
+ if (currencyInfo.isObsolete()) {
+ continue;
+ }
+ if (needHeader) {
+ needHeader = false;
+ writer.println();
+ writer.println("private void loadSuperCurrencyMap() {");
+ writer.println(" super.loadCurrencyMap();");
+ writer.println("}");
+ writer.println();
+ writer.println("@Override");
+ writer.println("protected native void loadCurrencyMap() /*-{");
+ writer.indent();
+ writer.println("this.@com.google.gwt.i18n.client.impl." + className
+ + "::loadSuperCurrencyMap()();");
+ writer.println("this.@com.google.gwt.i18n.client.impl." + className
+ + "::overrideCurrencyMap(Lcom/google/gwt/core/client/"
+ + "JavaScriptObject;)({");
+ writer.indent();
+ }
+ writer.println("// " + currencyInfo.getDisplayName());
+ writer.println("\"" + quote(currencyCode) + "\": "
+ + currencyInfo.getJson() + ",");
+ }
+ if (!needHeader) {
+ writer.outdent();
+ writer.println("});");
+ writer.outdent();
+ writer.println("}-*/;");
+ }
}
/**
- * Load a single localized properties file, adding to an existing
- * LocalizedProperties object.
+ * Writes a loadNamesMap method for the current locale, based on its the
+ * supplied names map and its superclass (if any).
*
- * @param logger TreeLogger instance
- * @param classLoader class loader to use to find property file
- * @param propFile property file name
- * @param props existing LocalizedProperties object to add to
- * @return true if the properties were successfully read
- * @throws UnableToCompleteException if an error occurs reading the file
+ * If no new names are added for this locale over its superclass, the method
+ * is omitted entirely.
+ *
+ * @param className name of the class we are generating
+ * @param writer SourceWriter instance to use for writing the class
+ * @param currencies array of valid currency names in the order they should be
+ * listed
*/
- private boolean readProperties(TreeLogger logger, ClassLoader classLoader,
- String propFile, LocalizedProperties props)
- throws UnableToCompleteException {
- propFile += ".properties";
- InputStream str = null;
- try {
- str = classLoader.getResourceAsStream(propFile);
- if (str != null) {
- props.load(str, "UTF-8");
- return true;
+ private void writeNamesMethod(String className, SourceWriter writer,
+ String[] currencies, Map<String, CurrencyInfo> allCurrencyData) {
+ boolean needHeader = true;
+ for (String currencyCode : currencies) {
+ CurrencyInfo currencyInfo = allCurrencyData.get(currencyCode);
+ if (currencyInfo.isObsolete()) {
+ continue;
}
- } catch (UnsupportedEncodingException e) {
- // UTF-8 should always be defined
- logger.log(TreeLogger.ERROR, "UTF-8 encoding is not defined", e);
- throw new UnableToCompleteException();
- } catch (IOException e) {
- logger.log(TreeLogger.ERROR, "Exception reading " + propFile, e);
- throw new UnableToCompleteException();
- } finally {
- if (str != null) {
- try {
- str.close();
- } catch (IOException e) {
- logger.log(TreeLogger.ERROR, "Exception closing " + propFile, e);
- throw new UnableToCompleteException();
+ String displayName = currencyInfo.getDisplayName();
+ if (displayName != null && !currencyCode.equals(displayName)) {
+ if (needHeader) {
+ needHeader = false;
+ writer.println();
+ writer.println("private void loadSuperNamesMap() {");
+ writer.println(" super.loadNamesMap();");
+ writer.println("}");
+ writer.println();
+ writer.println("@Override");
+ writer.println("protected native void loadNamesMap() /*-{");
+ writer.indent();
+ writer.println("this.@com.google.gwt.i18n.client.impl." + className
+ + "::loadSuperNamesMap()();");
+ writer.println("this.@com.google.gwt.i18n.client.impl." + className
+ + "::overrideNamesMap(Lcom/google/gwt/core/"
+ + "client/JavaScriptObject;)({");
+ writer.indent();
}
+ writer.println("\"" + quote(currencyCode) + "\": \""
+ + quote(displayName) + "\",");
}
}
- return false;
- }
-
- /**
- * Load a chain of localized properties files, starting with the default and
- * adding locale components so inheritance is properly recognized.
- *
- * @param logger TreeLogger instance
- * @param propFilePrefix property file name prefix; locale is added to it
- * @return a LocalizedProperties object containing all properties
- * @throws UnableToCompleteException if an error occurs reading the file
- */
- private LocalizedProperties readProperties(TreeLogger logger,
- String propFilePrefix, GwtLocale locale, GwtLocale[] foundLocale)
- throws UnableToCompleteException {
- LocalizedProperties props = new LocalizedProperties();
- ClassLoader classLoader = getClass().getClassLoader();
- List<GwtLocale> searchList = locale.getCompleteSearchList();
- GwtLocale lastFound = LocaleUtils.getLocaleFactory().fromString(null);
- for (int i = searchList.size(); i-- > 0; ) {
- GwtLocale search = searchList.get(i);
- String propFile = propFilePrefix;
- if (!search.isDefault()) {
- propFile += "_" + search.getAsString();
- }
- if (readProperties(logger, classLoader, propFile, props)) {
- lastFound = search;
- }
+ if (!needHeader) {
+ writer.outdent();
+ writer.println("});");
+ writer.outdent();
+ writer.println("}-*/;");
}
- foundLocale[0] = lastFound;
- return props;
}
}
diff --git a/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java b/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java
index 448512b..01a89ca 100644
--- a/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java
+++ b/user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java
@@ -37,6 +37,42 @@
// the property provider to handle inheritance there.
/**
+ * 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.
+ *
+ * Source: http://www.loc.gov/standards/iso639-2/php/code_changes.php
+ *
+ * TODO: consider building maps if this list grows much.
+ */
+ private static final String[] deprecatedLanguages = new String[] {
+ "he", "iw", // Hebrew
+ "id", "in", // Indonesian
+ "jv", "jw", // Javanese, typo in original publication
+ "ro", "mo", // Moldovian
+ "yi", "ji", // Yiddish
+ };
+
+ /**
+ * Maps deprecated region 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.
+ *
+ * Note that any mappings which split an old code into multiple new codes
+ * 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.
+ *
+ * Source: http://en.wikipedia.org/wiki/ISO_3166-1
+ *
+ * TODO: consider building maps if this list grows much.
+ */
+ private static final String[] deprecatedRegions = new String[] {
+ // East Timor - http://www.iso.org/iso/newsletter_v-5_east_timor.pdf
+ "TL", "TP",
+ };
+
+ /**
* Add in the missing locale of a deprecated pair.
*
* @param factory GwtLocaleFactory to create new instances with
@@ -45,36 +81,18 @@
*/
private static void addDeprecatedPairs(GwtLocaleFactory factory,
GwtLocale locale, List<GwtLocale> aliases) {
- if ("he".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("iw", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("iw".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("he", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("id".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("in", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("in".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("id", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("mo".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("ro", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("ro".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("mo", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("jv".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("jw", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("jw".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("jv", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("ji".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("yi", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
- } else if ("yi".equals(locale.getLanguage())) {
- aliases.add(factory.fromComponents("ji", locale.getScript(),
- locale.getRegion(), locale.getVariant()));
+ int n = deprecatedLanguages.length;
+ for (int i = 0; i < n; i += 2) {
+ if (deprecatedLanguages[i].equals(locale.getLanguage())) {
+ aliases.add(factory.fromComponents(deprecatedLanguages[i + 1],
+ locale.getScript(), locale.getRegion(), locale.getVariant()));
+ break;
+ }
+ if (deprecatedLanguages[i + 1].equals(locale.getLanguage())) {
+ aliases.add(factory.fromComponents(deprecatedLanguages[i],
+ locale.getScript(), locale.getRegion(), locale.getVariant()));
+ break;
+ }
}
}
@@ -159,10 +177,12 @@
}
}
} else if ("no".equals(language)) {
- if ("BOKMAL".equals(variant)) {
+ if (variant == null || "BOKMAL".equals(variant)) {
aliases.add(factory.fromComponents("nb", script, region, null));
+ aliases.add(factory.fromComponents("no-bok", script, region, null));
} else if ("NYNORSK".equals(variant)) {
aliases.add(factory.fromComponents("nn", script, region, null));
+ aliases.add(factory.fromComponents("no-nyn", script, region, null));
}
} else if ("nb".equals(language)) {
aliases.add(factory.fromComponents("no", script, region, "BOKMAL"));
@@ -262,7 +282,9 @@
// 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
@@ -283,7 +305,9 @@
continue;
}
seen.add(locale);
- cachedAliases.add(locale);
+ if (!locale.equals(canonicalForm)) {
+ cachedAliases.add(locale);
+ }
addDeprecatedPairs(factory, locale, nextGroup);
addSpecialAliases(factory, locale, nextGroup);
}
@@ -312,6 +336,77 @@
return buf.toString();
}
+ /**
+ * Returns this locale in canonical form:
+ * <ul>
+ * <li>Deprecated language/region tags are replaced with official versions
+ * <li>
+ * </ul>
+ *
+ * @return GwtLocale instance
+ */
+ public GwtLocale getCanonicalForm() {
+ String canonLanguage = language;
+ String canonScript = script;
+ String canonRegion = region;
+ String canonVariant = variant;
+ // Handle deprecated language codes
+ int n = deprecatedLanguages.length;
+ for (int i = 0; i < n; i += 2) {
+ if (deprecatedLanguages[i + 1].equals(canonLanguage)) {
+ canonLanguage = deprecatedLanguages[i];
+ break;
+ }
+ }
+ // Handle deprecated region codes
+ n = deprecatedRegions.length;
+ for (int i = 0; i < n; i += 2) {
+ if (deprecatedRegions[i + 1].equals(canonRegion)) {
+ canonRegion = deprecatedRegions[i];
+ break;
+ }
+ }
+ // Special-case Chinese default scripts
+ if ("zh".equals(canonLanguage)) {
+ if (canonRegion != null) {
+ if ("CN".equals(canonRegion) && "Hans".equals(canonScript)) {
+ canonScript = null;
+ } else if ("TW".equals(canonRegion) && "Hant".equals(canonScript)) {
+ canonScript = null;
+ }
+ } else if ("Hans".equals(canonScript)) {
+ canonRegion = "CN";
+ canonScript = null;
+ } else if ("Hant".equals(canonScript)) {
+ canonRegion = "TW";
+ canonScript = null;
+ }
+ }
+ // Special-case no->nb/nn split
+ if ("no-bok".equals(canonLanguage)) {
+ canonLanguage = "nb";
+ canonVariant = null;
+ } else if ("no-nyn".equals(canonLanguage)) {
+ canonLanguage = "nn";
+ canonVariant = null;
+ } else if ("no".equals(canonLanguage)) {
+ if (canonVariant == null || "BOKMAL".equals(canonVariant)) {
+ canonLanguage = "nb";
+ canonVariant = null;
+ } else if ("NYNORSK".equals(canonVariant)) {
+ canonLanguage = "nn";
+ canonVariant = null;
+ }
+ }
+ // Remove any default script for the language
+ if (canonScript != null && canonScript.equals(
+ DefaultLanguageScripts.getDefaultScript(canonLanguage))) {
+ canonScript = null;
+ }
+ return factory.fromComponents(canonLanguage, canonScript, canonRegion,
+ canonVariant);
+ }
+
public List<GwtLocale> getCompleteSearchList() {
// TODO(jat): get zh_Hant to come before zh in search list for zh_TW
if (cachedSearchList == null) {
diff --git a/user/src/com/google/gwt/i18n/shared/GwtLocale.java b/user/src/com/google/gwt/i18n/shared/GwtLocale.java
index 35ca036..8dd7bb4 100644
--- a/user/src/com/google/gwt/i18n/shared/GwtLocale.java
+++ b/user/src/com/google/gwt/i18n/shared/GwtLocale.java
@@ -32,8 +32,8 @@
int compareTo(GwtLocale o);
/**
- * Return the list of aliases for this locale. The current locale is always
- * first on the list.
+ * Return the list of aliases for this locale. The canonical form of the
+ * current locale is always first on the list.
*
* Language/region codes have changed over time, so some systems continue to
* use the older codes. Aliases allow GWT to use the official Unicode CLDR
@@ -43,8 +43,41 @@
*/
List<GwtLocale> getAliases();
+ /**
+ * Return the locale as a fixed-format string suitable for use in searching
+ * for localized resources. The format is language_Script_REGION_VARIANT,
+ * where language is a 2-8 letter code (possibly with 3-letter extensions),
+ * script is a 4-letter code with an initial capital letter, region is a
+ * 2-character country code or a 3-digit region code, and variant is a 5-8
+ * character (may be 4 if the first character is numeric) code. If a
+ * component is missing, its preceding _ is also omitted. If this is the
+ * default locale, the empty string will be returned.
+ *
+ * @return String representing locale
+ */
String getAsString();
+ /**
+ * Returns this locale in canonical form.
+ * <ul>
+ * <li>Deprecated language/region tags are replaced with official versions
+ * <li>Default scripts are removed (including region-specific defaults for
+ * Chinese)
+ * <li>no/nb/nn are normalized
+ * <li>Default region for zh_Hans and zh_Hant if none specified
+ * </ul>
+ *
+ * @return GwtLocale instance
+ */
+ GwtLocale getCanonicalForm();
+
+ /**
+ * Return the complete list of locales to search for the current locale.
+ * This list will always start with the canonical form of this locale, and
+ * end with "default", and include all appropriate aliases along the way.
+ *
+ * @return search list
+ */
List<GwtLocale> getCompleteSearchList();
/**
@@ -56,20 +89,44 @@
*/
List<GwtLocale> getInheritanceChain();
+ /**
+ * @return the language portion of the locale, or null if none.
+ */
String getLanguage();
+ /**
+ * @return the language portion of the locale, or the empty string if none.
+ */
String getLanguageNotNull();
+ /**
+ * @return the region portion of the locale, or null if none.
+ */
String getRegion();
+ /**
+ * @return the region portion of the locale, or the empty string if none.
+ */
String getRegionNotNull();
+ /**
+ * @return the script portion of the locale, or null if none.
+ */
String getScript();
+ /**
+ * @return the script portion of the locale, or the empty string if none.
+ */
String getScriptNotNull();
+ /**
+ * @return the variant portion of the locale, or null if none.
+ */
String getVariant();
+ /**
+ * @return the variant portion of the locale, or the empty string if none.
+ */
String getVariantNotNull();
/**
@@ -82,8 +139,14 @@
*/
boolean inheritsFrom(GwtLocale parent);
+ /**
+ * @return true if this is the default or root locale.
+ */
boolean isDefault();
+ /**
+ * @return a human readable string -- "default" or the same as getAsString().
+ */
String toString();
/**
diff --git a/user/test/com/google/gwt/i18n/I18NTest_es_AR_runtime.gwt.xml b/user/test/com/google/gwt/i18n/I18NTest_es_AR_runtime.gwt.xml
new file mode 100644
index 0000000..9cddf0b
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/I18NTest_es_AR_runtime.gwt.xml
@@ -0,0 +1,25 @@
+<!-- -->
+<!-- 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 -->
+<!-- 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. License for the specific language governing permissions and -->
+<!-- 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="es_419"/>
+ <set-property name = "locale" value = "es_419"/>
+ <set-configuration-property name="runtime.locales" value="es_AR,es_MX"/>
+ <public path="public_es_AR"/>
+</module>
diff --git a/user/test/com/google/gwt/i18n/client/I18N_es_AR_RuntimeTest.java b/user/test/com/google/gwt/i18n/client/I18N_es_AR_RuntimeTest.java
new file mode 100644
index 0000000..f3dfe62
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/client/I18N_es_AR_RuntimeTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2007 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.i18n.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.I18N_es_MX_Test.MyConstants;
+import com.google.gwt.i18n.client.I18N_es_MX_Test.MyMessages;
+import com.google.gwt.i18n.client.impl.CurrencyData;
+import com.google.gwt.i18n.client.impl.CurrencyList;
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests regional inheritance for es_AR.
+ */
+public class I18N_es_AR_RuntimeTest extends GWTTestCase {
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.i18n.I18NTest_es_AR_runtime";
+ }
+
+ public void testAvailableLocales() {
+ String[] locales = LocaleInfo.getAvailableLocaleNames();
+ Set<String> localeSet = new HashSet<String>();
+ List<String> localeList = Arrays.asList(locales);
+ localeSet.addAll(localeList);
+ List<String> expectedList = Arrays.asList("default", "es_419", "es_AR",
+ "es_MX");
+ assertEquals(expectedList.size(), localeSet.size());
+ localeSet.removeAll(expectedList);
+ assertEquals(0, localeSet.size());
+ }
+
+ public void testCurrencyNames() {
+ assertEquals("Peso Argentino", CurrencyList.get().lookupName("ARS"));
+ assertEquals("peso mexicano", CurrencyList.get().lookupName("MXN"));
+ assertEquals("dólar estadounidense", CurrencyList.get().lookupName("USD"));
+ }
+
+ public void testDefaultCurrency() {
+ CurrencyData data = CurrencyList.get().getDefault();
+ assertEquals("ARS", data.getCurrencyCode());
+ assertEquals("$", data.getCurrencySymbol());
+ assertEquals(2, data.getDefaultFractionDigits());
+ }
+
+ public void testOtherCurrency() {
+ CurrencyData ars = CurrencyList.get().lookup("ARS");
+ assertEquals("ARS", ars.getCurrencyCode());
+ assertEquals("$", ars.getCurrencySymbol());
+ assertEquals(2, ars.getDefaultFractionDigits());
+ CurrencyData data = CurrencyList.get().lookup("MXN");
+ assertEquals("MXN", data.getCurrencyCode());
+ assertEquals("MEX$", data.getCurrencySymbol());
+ assertEquals(2, data.getDefaultFractionDigits());
+ CurrencyData usd = CurrencyList.get().lookup("USD");
+ assertEquals("USD", usd.getCurrencyCode());
+ assertEquals("US$", usd.getCurrencySymbol());
+ assertEquals(2, usd.getDefaultFractionDigits());
+ boolean found = false;
+ for (CurrencyData it : CurrencyList.get()) {
+ if ("USD".equals(it.getCurrencyCode())) {
+ assertEquals("US$", it.getCurrencySymbol());
+ assertEquals(2, it.getDefaultFractionDigits());
+ found = true;
+ break;
+ }
+ }
+ assertTrue("Did not find USD in iterator", found);
+ }
+
+ public void testRegionalInheritance() {
+ MyMessages msg = GWT.create(MyMessages.class);
+ assertEquals("es_419", msg.getSourceLocale());
+ MyConstants cst = GWT.create(MyConstants.class);
+ // Since our copile-time locale is es_419 (Latin America), we do
+ // not get es_019 (Central America) in the inheritance chain for
+ // es_AR as only the compile-time locales are used for translation
+ // inheritance.
+ assertEquals("default", cst.getSourceLocale());
+ }
+
+ public void testRuntimeLocale() {
+ assertEquals("es_AR", LocaleInfo.getCurrentLocale().getLocaleName());
+ }
+}
diff --git a/user/test/com/google/gwt/i18n/client/I18N_es_MX_RuntimeTest.java b/user/test/com/google/gwt/i18n/client/I18N_es_MX_RuntimeTest.java
index 18079e1..2cba95f 100644
--- a/user/test/com/google/gwt/i18n/client/I18N_es_MX_RuntimeTest.java
+++ b/user/test/com/google/gwt/i18n/client/I18N_es_MX_RuntimeTest.java
@@ -49,12 +49,43 @@
assertEquals(0, localeSet.size());
}
+ public void testCurrencyNames() {
+ assertEquals("peso argentino", CurrencyList.get().lookupName("ARS"));
+ assertEquals("peso mexicano", CurrencyList.get().lookupName("MXN"));
+ assertEquals("dólar estadounidense", CurrencyList.get().lookupName("USD"));
+ }
+
public void testDefaultCurrency() {
CurrencyData data = CurrencyList.get().getDefault();
assertEquals("MXN", data.getCurrencyCode());
assertEquals("$", data.getCurrencySymbol());
assertEquals(2, data.getDefaultFractionDigits());
}
+
+ public void testOtherCurrency() {
+ CurrencyData ars = CurrencyList.get().lookup("ARS");
+ assertEquals("ARS", ars.getCurrencyCode());
+ assertEquals("Arg$", ars.getCurrencySymbol());
+ assertEquals(2, ars.getDefaultFractionDigits());
+ CurrencyData data = CurrencyList.get().lookup("MXN");
+ assertEquals("MXN", data.getCurrencyCode());
+ assertEquals("$", data.getCurrencySymbol());
+ assertEquals(2, data.getDefaultFractionDigits());
+ CurrencyData usd = CurrencyList.get().lookup("USD");
+ assertEquals("USD", usd.getCurrencyCode());
+ assertEquals("US$", usd.getCurrencySymbol());
+ assertEquals(2, usd.getDefaultFractionDigits());
+ boolean found = false;
+ for (CurrencyData it : CurrencyList.get()) {
+ if ("USD".equals(it.getCurrencyCode())) {
+ assertEquals("US$", it.getCurrencySymbol());
+ assertEquals(2, it.getDefaultFractionDigits());
+ found = true;
+ break;
+ }
+ }
+ assertTrue("Did not find USD in iterator", found);
+ }
public void testRegionalInheritance() {
MyMessages msg = GWT.create(MyMessages.class);
diff --git a/user/test/com/google/gwt/i18n/public_es_AR/junit.html b/user/test/com/google/gwt/i18n/public_es_AR/junit.html
new file mode 100644
index 0000000..b316a47
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/public_es_AR/junit.html
@@ -0,0 +1,67 @@
+<!--
+Copyright 2008 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.
+-->
+<html>
+<head>
+<meta name='gwt:onLoadErrorFn' content='junitOnLoadErrorFn'>
+<meta name='gwt:onPropertyErrorFn' content='junitOnPropertyErrorFn'>
+</head>
+<body>
+<script language='javascript'>
+<!--
+// -- BEGIN CHANGE FROM junit/public/junit.html
+// Set the runtime locale for this test
+// TODO(jat): find a better way to do this that doesn't require duplcating
+// junit.html for each different runtime locale.
+window['__gwt_Locale'] = 'es_AR';
+// -- END CHANGE FROM junit/public/junit.html
+
+function junitOnLoadErrorFn(moduleName) {
+ junitError('Failed to load module "' + moduleName +
+ '".\nPlease see the log for details.');
+}
+
+function junitOnPropertyErrorFn(propName, allowedValues, badValue) {
+ var msg = 'While attempting to load the module, property "' + propName;
+ if (badValue != null) {
+ msg += '" was set to the unexpected value "' + badValue + '"';
+ } else {
+ msg += '" was not specified';
+ }
+ msg += 'Allowed values: ' + allowedValues;
+ junitError(msg);
+}
+
+function junitError(msg) {
+ var xmlHttpRequest = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
+ xmlHttpRequest.open('POST', 'junithost/loadError', true);
+ xmlHttpRequest.setRequestHeader('Content-Type', 'text/x-gwt-rpc; charset=utf-8');
+ xmlHttpRequest.send(msg);
+}
+
+function loadSelectionScript() {
+ var moduleName = document.location.href;
+ var pos = moduleName.lastIndexOf('/');
+ moduleName = moduleName.substr(0, pos);
+ pos = moduleName.lastIndexOf('/');
+ moduleName = moduleName.substr(pos + 1);
+ document.write("<script language='javascript' src='" + moduleName + ".nocache.js'></script>");
+}
+loadSelectionScript();
+-->
+</script>
+<iframe src="javascript:''" id='__gwt_historyFrame' style='position:absolute;width:0;height:0;border:0'></iframe>
+</body>
+</html>
diff --git a/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java b/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java
index 3bf1298..ee2eaec 100644
--- a/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java
+++ b/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java
@@ -38,12 +38,13 @@
assertContainsAndGetPosition(aliases, factory.fromString("zh_Hans_CN"));
GwtLocale zhHant = factory.fromString("zh_Hant");
aliases = zhHant.getAliases();
- assertEquals(aliases.get(0), zhHant);
+ assertEquals(aliases.get(0), factory.fromString("zh_TW"));
assertContainsAndGetPosition(aliases, factory.fromString("zh_Hant_TW"));
GwtLocale zhHans = factory.fromString("zh_Hans");
aliases = zhHans.getAliases();
- assertEquals(aliases.get(0), zhHans);
+ assertEquals(aliases.get(0), zhCN);
assertContainsAndGetPosition(aliases, factory.fromString("zh_Hans_CN"));
+ assertContainsAndGetPosition(aliases, zhHans);
GwtLocale en = factory.fromString("en");
aliases = en.getAliases();
assertEquals(aliases.get(0), en);
@@ -56,7 +57,11 @@
GwtLocale pt = factory.fromString("pt");
aliases = pt.getAliases();
assertContainsAndGetPosition(aliases, factory.fromString("pt_BR"));
+ GwtLocale iw = factory.fromString("iw");
+ aliases = iw.getAliases();
GwtLocale he = factory.fromString("he");
+ assertEquals(aliases.get(0), he);
+ assertContainsAndGetPosition(aliases, factory.fromString("iw_Hebr"));
aliases = he.getAliases();
assertContainsAndGetPosition(aliases, factory.fromString("iw_Hebr"));
GwtLocale id = factory.fromString("id");