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.
Resubmit of r5174 after compiler fixes were committed.
Patch by: jat, scottb
Review by: scottb, jat
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5265 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");