blob: d4f25f6acf76c2ff75ee938b26a6649bf0060ceb [file] [log] [blame]
/*
* Copyright 2010 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.tools.cldr;
import com.google.gwt.i18n.shared.GwtLocale;
import com.google.gwt.i18n.shared.GwtLocaleFactory;
import org.unicode.cldr.util.XPathParts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Collects all the locale data from CLDR, grouping it by how it will be used
* and removing equivalent values that could be inherited.
*/
public class LocaleData {
/**
* Represents data about a single currency in a particular locale from CLDR.
*/
public static class Currency {
private static boolean equalsNullCheck(Object a, Object b) {
if (a == null) {
return b == null;
}
return a.equals(b);
}
private static int hashCodeNullCheck(Object obj) {
return obj == null ? 0 : obj.hashCode();
}
private final String code;
private int decimalDigits;
private String decimalSeparator;
private String displayName;
private String groupingSeparator;
private boolean inUse;
private String pattern;
private int rounding;
private String symbol;
public Currency(String code) {
this.code = code;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Currency)) {
return false;
}
Currency other = (Currency) obj;
return code.equals(other.code) && equalsNullCheck(displayName, other.displayName)
&& equalsNullCheck(symbol, other.symbol) && equalsNullCheck(pattern, other.pattern)
&& equalsNullCheck(decimalSeparator, other.decimalSeparator)
&& equalsNullCheck(groupingSeparator, other.groupingSeparator)
&& decimalDigits == other.decimalDigits && inUse == other.inUse
&& rounding == other.rounding;
}
public String getCode() {
return code;
}
/**
* @return the number of decimal digits this currency is commonly displayed
* with.
*/
public int getDecimalDigits() {
return decimalDigits;
}
public String getDecimalSeparator() {
return decimalSeparator;
}
public String getDisplayName() {
return displayName;
}
public String getGroupingSeparator() {
return groupingSeparator;
}
public String getPattern() {
return pattern;
}
public int getRounding() {
return rounding;
}
public String getSymbol() {
return symbol;
}
@Override
public int hashCode() {
return code.hashCode() + 17 * hashCodeNullCheck(displayName) + 19 * hashCodeNullCheck(symbol)
+ 23 * hashCodeNullCheck(pattern) + 29 * hashCodeNullCheck(decimalSeparator) + 31
* hashCodeNullCheck(groupingSeparator) + 37 * decimalDigits + (inUse ? 41 : 0)
+ 43 * rounding;
}
/**
* @return true if this currency is still in regular use.
*/
public boolean isInUse() {
return inUse;
}
public void setDecimalDigits(int decimalDigits) {
this.decimalDigits = decimalDigits;
}
public void setDecimalSeparator(String decimalSeparator) {
this.decimalSeparator = decimalSeparator;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public void setGroupingSeparator(String groupingSeparator) {
this.groupingSeparator = groupingSeparator;
}
public void setInUse(boolean inUse) {
this.inUse = inUse;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public void setRounding(int rounding) {
this.rounding = rounding;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(code);
if (displayName != null || symbol != null) {
buf.append(" (");
}
if (displayName != null) {
buf.append(displayName);
}
if (symbol != null) {
if (displayName != null) {
buf.append("; ");
}
buf.append(symbol);
}
if (displayName != null || symbol != null) {
buf.append(")");
}
return buf.toString();
}
}
/**
* Comparator that orders locales based the inheritance depth.
*/
private class LocaleComparator implements Comparator<GwtLocale> {
@Override
public int compare(GwtLocale a, GwtLocale b) {
Integer depthA = localeDepth.get(a);
Integer depthB = localeDepth.get(b);
int c = 0;
if (depthA != null && depthB != null) {
c = depthB - depthA;
}
if (c == 0) {
c = a.compareTo(b);
}
return c;
}
}
/**
* Encapsulates the key for lookup values, comprising a locale and a category.
*/
private static class MapKey {
private final String category;
private final GwtLocale locale;
public MapKey(String category, GwtLocale locale) {
this.category = category;
this.locale = locale;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
MapKey other = (MapKey) obj;
return locale.equals(other.locale) && category.equals(other.category);
}
public String getCategory() {
return category;
}
public GwtLocale getLocale() {
return locale;
}
@Override
public int hashCode() {
return category.hashCode() + 31 * locale.hashCode();
}
public MapKey inNewLocale(GwtLocale newLocale) {
return new MapKey(category, newLocale);
}
@Override
public String toString() {
return "[cat=" + category + ", locale=" + locale + "]";
}
}
/**
* Return the CLDR locale name for a GWT locale.
*
* @param locale
* @return CLDR locale name for GWT locale
*/
public static String getCldrLocale(GwtLocale locale) {
return locale.isDefault() ? "root" : locale.toString();
}
/**
* Get the value of a given category of territory data inherited by a locale.
*
* @param locale the locale to search for
* @param map the map containing territory=>value data
* @return the requested value from the closest ancestor of the specified
* locale, or null if not found
*/
private static String getTerritoryData(GwtLocale locale, Map<String, String> map) {
if (map == null) {
return null;
}
for (GwtLocale search : locale.getCompleteSearchList()) {
String region = search.getRegion();
if (region == null) {
region = "001";
}
String value = map.get(region);
if (value != null) {
return value;
}
}
return null;
}
private final Map<GwtLocale, String> allLocales;
private final GwtLocale defaultLocale;
private final HashMap<GwtLocale, GwtLocale> inheritsFrom;
private final Map<GwtLocale, Integer> localeDepth;
private final GwtLocaleFactory localeFactory;
private Map<MapKey, Map<String, String>> maps;
/**
* Construct a LocaleData object.
*
* @param localeFactory
* @param localeNames
*/
public LocaleData(GwtLocaleFactory localeFactory, Collection<String> localeNames) {
this.localeFactory = localeFactory;
defaultLocale = localeFactory.getDefault();
allLocales = new HashMap<GwtLocale, String>();
for (String localeName : localeNames) {
allLocales.put(getGwtLocale(localeName), localeName);
}
inheritsFrom = new HashMap<GwtLocale, GwtLocale>();
buildInheritsFrom();
localeDepth = new HashMap<GwtLocale, Integer>();
maps = new HashMap<MapKey, Map<String, String>>();
buildLocaleDepth();
}
/**
* Add a single entry from an attribute on a CLDR node.
*
* @param category
* @param locale
* @param cldrFactory
* @param path
* @param tag
* @param key
* @param attribute
*/
void addAttributeEntry(String category, GwtLocale locale, InputFactory cldrFactory,
String path, String tag, String key, String attribute, String defaultValue) {
Map<String, String> map = getMap(category, locale);
InputFile cldr = cldrFactory.load(allLocales.get(locale));
XPathParts parts = new XPathParts();
String fullPath = cldr.getFullXPath(path);
Map<String, String> attr = null;
if (fullPath != null) {
parts.set(fullPath);
attr = parts.findAttributes(tag);
}
String value;
if (attr != null) {
value = attr.get(attribute);
} else if (defaultValue != null) {
value = defaultValue;
} else {
return;
}
map.put(key, value);
}
/**
* Add currency entries for all locales.
*
* @param currencyFractions map of currency fraction data extracted from
* locale-independent data
*/
void addCurrencyEntries(String category, InputFactory inputFactory,
Map<String, Integer> currencyFractions, int defaultCurrencyFraction, Set<String> stillInUse,
Map<String, Integer> rounding) {
for (GwtLocale locale : allLocales.keySet()) {
// skip the "default" locale for now
if (locale.isDefault()) {
continue;
}
addCurrencyEntries(category, locale, inputFactory, currencyFractions, defaultCurrencyFraction,
stillInUse, rounding);
}
// run the "default" locale last, to override inherited entries
GwtLocale locale = localeFactory.getDefault();
addCurrencyEntries(category, locale, inputFactory, currencyFractions, defaultCurrencyFraction,
stillInUse, rounding);
}
void addDateTimeFormatEntries(String group, InputFactory cldrFactory) {
addAttributeEntries(group, cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/"
+ group + "Formats/default", "default", "default", "choice", "medium");
addDateTimeFormatEntries(group, "full", cldrFactory);
addDateTimeFormatEntries(group, "long", cldrFactory);
addDateTimeFormatEntries(group, "medium", cldrFactory);
addDateTimeFormatEntries(group, "short", cldrFactory);
}
void addEntries(String category, InputFactory cldrFactory, String prefix, String tag,
String keyAttribute) {
for (GwtLocale locale : allLocales.keySet()) {
addEntries(category, locale, cldrFactory, prefix, tag, keyAttribute);
}
}
void addEntries(String category, GwtLocale locale, InputFactory cldrFactory, String prefix,
String tag, String keyAttribute) {
Map<String, String> map = getMap(category, locale);
InputFile cldr = cldrFactory.load(allLocales.get(locale));
XPathParts parts = new XPathParts();
for (String path : cldr.listPaths(prefix)) {
String fullXPath = cldr.getFullXPath(path);
if (fullXPath == null) {
fullXPath = path;
}
parts.set(fullXPath);
if (parts.containsAttribute("alt")) {
// ignore alternate strings
continue;
}
Map<String, String> attr = parts.findAttributes(tag);
if (attr == null) {
continue;
}
String value = cldr.getStringValue(path);
boolean draft = parts.containsAttribute("draft");
String key = keyAttribute != null ? attr.get(keyAttribute) : "value";
if (!draft || !map.containsKey(key)) {
map.put(key, value);
}
}
}
public void addEntry(String category, GwtLocale locale, String key, String value) {
Map<String, String> map = getMap(category, locale);
map.put(key, value);
}
/**
* @param period "month", "day", "quarter", "dayPeriod",
*/
void addNameEntries(String period, InputFactory cldrFactory) {
addEntries(period + "-abbrev", cldrFactory,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period + "s/" + period
+ "Context[@type=\"format\"]/" + period + "Width[@type=\"abbreviated\"]", period,
"type");
addEntries(period + "-narrow", cldrFactory,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period + "s/" + period
+ "Context[@type=\"format\"]/" + period + "Width[@type=\"narrow\"]", period, "type");
addEntries(period + "-wide", cldrFactory,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period + "s/" + period
+ "Context[@type=\"format\"]/" + period + "Width[@type=\"wide\"]", period, "type");
addEntries(period + "-sa-abbrev", cldrFactory,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period + "s/" + period
+ "Context[@type=\"stand-alone\"]/" + period + "Width[@type=\"abbreviated\"]", period,
"type");
addEntries(period + "-sa-narrow", cldrFactory,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period + "s/" + period
+ "Context[@type=\"stand-alone\"]/" + period + "Width[@type=\"narrow\"]", period,
"type");
addEntries(period + "-sa-wide", cldrFactory,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period + "s/" + period
+ "Context[@type=\"stand-alone\"]/" + period + "Width[@type=\"wide\"]", period, "type");
}
/**
* Add entries from territory-oriented CLDR data.
*
* @param category category to store resulting data under
* @param cldrFactory
* @param regionLanguageData
* @param prefix the XPath prefix to iterate through
* @param tag the tag to load
* @param keyAttribute the attribute in the tag to use as the key
*/
public void addTerritoryEntries(String category, InputFactory cldrFactory,
RegionLanguageData regionLanguageData, String prefix, String tag, String keyAttribute) {
InputFile supp = cldrFactory.getSupplementalData();
Map<String, String> map = new HashMap<String, String>();
XPathParts parts = new XPathParts();
for (String path : supp.listPaths(prefix)) {
parts.set(supp.getFullXPath(path));
Map<String, String> attr = parts.findAttributes(tag);
if (attr == null || attr.get("alt") != null) {
continue;
}
String key = attr.get(keyAttribute);
String territories = attr.get("territories");
String draft = attr.get("draft");
for (String territory : territories.split(" ")) {
if (draft == null || !map.containsKey(territory)) {
map.put(territory, key);
}
}
}
if (regionLanguageData != null) {
// find the choice used by most literate speakers of each language
// based on region-based preferences.
summarizeTerritoryEntries(category, regionLanguageData, tag, map);
}
}
void addVersions(InputFactory inputFactory) {
for (GwtLocale locale : allLocales.keySet()) {
Map<String, String> map = getMap("version", locale);
InputFile file = inputFactory.load(allLocales.get(locale));
XPathParts parts = new XPathParts();
for (String path : file.listPaths("//ldml/identity")) {
String fullXPath = file.getFullXPath(path);
if (fullXPath == null) {
fullXPath = path;
}
parts.set(fullXPath);
Map<String, String> attr = parts.getAttributes(2);
if (attr == null) {
continue;
}
for (Map.Entry<String, String> entry : attr.entrySet()) {
map.put(entry.getKey(), entry.getValue());
}
}
}
}
/**
* Add a redirect entry for each locale where all entries in the standalone
* category match those in the base category.
*
* @param baseCategory
* @param standaloneCategory
*/
public void computeRedirects(String baseCategory, String standaloneCategory) {
for (GwtLocale locale : allLocales.keySet()) {
MapKey baseKey = new MapKey(baseCategory, locale);
MapKey standaloneKey = new MapKey(standaloneCategory, locale);
Map<String, String> baseMap = maps.get(baseKey);
Map<String, String> standaloneMap = maps.get(standaloneKey);
if (baseMap != null && standaloneMap != null
&& (standaloneMap.isEmpty() || baseMap.equals(standaloneMap))) {
addEntry(standaloneCategory + "-redirect", locale, "redirect", "yes");
}
}
}
/**
* Copy data from one locale to another.
*
* @param srcLocaleName source locale name
* @param destLocaleName destination locale name
* @param categories list of categories to copy
*/
public void copyLocaleData(String srcLocaleName, String destLocaleName, String... categories) {
GwtLocale src = localeFactory.fromString(srcLocaleName);
GwtLocale dest = localeFactory.fromString(destLocaleName);
for (String category : categories) {
Map<String, String> srcMap = maps.get(new MapKey(category, src));
if (srcMap == null || srcMap.isEmpty()) {
continue;
}
Map<String, String> destMap = getMap(category, dest);
destMap.putAll(srcMap);
}
}
public Map<String, Map<String, String>> getAllEntries(String localeName) {
GwtLocale locale = localeFactory.fromString(localeName);
Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
Map<String, String> map = entry.getValue();
if (entry.getKey().getLocale().equals(locale) && !map.isEmpty()) {
result.put(entry.getKey().getCategory(), Collections.unmodifiableMap(entry.getValue()));
}
}
return result;
}
/**
* @return all locales present in the CLDR data.
*/
public Set<GwtLocale> getAllLocales() {
return Collections.unmodifiableSet(allLocales.keySet());
}
/**
* Return all entries in a given category and locale.
*
* @param category
* @param locale
* @return map of keys to localized values
*/
public Map<String, String> getEntries(String category, GwtLocale locale) {
MapKey mapKey = new MapKey(category, locale);
Map<String, String> map = maps.get(mapKey);
if (map == null) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(map);
}
/**
* Returns the keys of all entries in a given cateogry and locale.
*/
List<String> getKeys(String category, GwtLocale locale) {
MapKey mapKey = new MapKey(category, locale);
Map<String, String> map = maps.get(mapKey);
if (map == null) {
return new ArrayList<String>();
}
ArrayList<String> out = new ArrayList<String>(map.keySet());
Collections.sort(out);
return out;
}
/**
* Return a single value.
*
* @param category
* @param locale
* @param key
* @return the requested value, or null if not present
*/
public String getEntry(String category, GwtLocale locale, String key) {
MapKey mapKey = new MapKey(category, locale);
Map<String, String> map = maps.get(mapKey);
if (map == null) {
return null;
}
return map.get(key);
}
/**
* @param localeName
* @return GwtLocale instance for CLDR locale
*/
public GwtLocale getGwtLocale(String localeName) {
return "root".equals(localeName) ? localeFactory.getDefault() : localeFactory
.fromString(localeName);
}
/**
* Return a single value, following locale inheritance.
*
* @param category
* @param locale
* @param key
* @return the requested value, or null if not present
*/
public String getInheritedEntry(String category, GwtLocale locale, String key) {
for (GwtLocale search : locale.getCompleteSearchList()) {
String value = getEntry(category, search, key);
if (value != null) {
return value;
}
}
return null;
}
/**
* @return all locales that have some data associated with them.
*/
public Set<GwtLocale> getNonEmptyLocales() {
Set<GwtLocale> result = new HashSet<GwtLocale>();
for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
Map<String, String> map = entry.getValue();
// Ignore version entries
if ("version".equals(entry.getKey().getCategory()) || map.isEmpty()) {
continue;
}
result.add(entry.getKey().getLocale());
}
return result;
}
/**
* @param category
* @return all locales that have some data associated with them in the
* specified category.
*/
public Set<GwtLocale> getNonEmptyLocales(String category) {
Set<GwtLocale> result = new HashSet<GwtLocale>();
for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
Map<String, String> map = entry.getValue();
if (!category.equals(entry.getKey().category) || map.isEmpty()) {
continue;
}
result.add(entry.getKey().getLocale());
}
return result;
}
/**
* Return the nearest ancestor locale of the supplied locale which has any
* values present.
*
* @param locale
* @return GwtLocale of nearest ancestor
*/
public GwtLocale inheritsFrom(GwtLocale locale) {
GwtLocale parent = inheritsFrom.get(locale);
while (parent != null && parent != defaultLocale) {
for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
MapKey key = entry.getKey();
// Version entries are always present, so ignore them
if (!"version".equals(key.getCategory()) && key.getLocale().equals(parent)) {
Map<String, String> map = entry.getValue();
if (!map.isEmpty()) {
return parent;
}
}
}
parent = inheritsFrom.get(parent);
}
return parent;
}
/**
* Return the nearest ancestor locale of the supplied locale which has any
* values present in the specified category.
*
* @param category
* @param locale
* @return GwtLocale of nearest ancestor with the specified category
*/
public GwtLocale inheritsFrom(String category, GwtLocale locale) {
GwtLocale parent = inheritsFrom.get(locale);
while (parent != null && parent != defaultLocale) {
Map<String, String> map = getMap(category, parent);
if (!map.isEmpty()) {
return parent;
}
parent = inheritsFrom.get(parent);
}
return parent;
}
/**
* Remove locale entries that completely duplicate their parent.
*/
public void removeCompleteDuplicates() {
removeCompleteDuplicates(null);
}
/**
* Remove locale entries that completely duplicate their parent.
*
* @param matchCategory
*/
public void removeCompleteDuplicates(String matchCategory) {
MapKey[] keys = getSortedMapKeys();
for (MapKey key : keys) {
String category = key.getCategory();
if (matchCategory != null && !matchCategory.equals(category)) {
continue;
}
GwtLocale locale = key.getLocale();
GwtLocale parent = inheritsFrom(category, locale);
if (parent == null) {
continue;
}
MapKey parentKey = key.inNewLocale(parent);
Map<String, String> parentMap = maps.get(parentKey);
if (parentMap == null) {
continue;
}
Map<String, String> map = maps.get(key);
boolean allMatch = true;
for (Map.Entry<String, String> entry : map.entrySet()) {
if (!entry.getValue().equals(parentMap.get(entry.getKey()))) {
allMatch = false;
break;
}
}
if (allMatch) {
maps.remove(key);
}
}
}
/**
* Remove entries that are duplicates of the entries in the parent locale.
*/
public void removeDuplicates() {
removeDuplicates(null);
}
/**
* Remove entries that are duplicates of the entries in the parent locale.
*
* @param matchCategory
*/
public void removeDuplicates(String matchCategory) {
MapKey[] keys = getSortedMapKeys();
for (MapKey key : keys) {
String category = key.getCategory();
if (matchCategory != null && !matchCategory.equals(category)) {
continue;
}
GwtLocale locale = key.getLocale();
GwtLocale parent = inheritsFrom(category, locale);
if (parent == null) {
continue;
}
MapKey parentKey = key.inNewLocale(parent);
Map<String, String> parentMap = maps.get(parentKey);
if (parentMap == null) {
continue;
}
Map<String, String> map = maps.get(key);
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
String value = entry.getValue();
if (value == null || value.equals(parentMap.get(entry.getKey()))) {
it.remove();
}
}
}
}
/**
* Remove entries in the specified category and locale which match any of the
* supplied keys.
*
* @param category
* @param locale
* @param keys
*/
public void removeEntries(String category, GwtLocale locale, Collection<String> keys) {
Map<String, String> map = getMap(category, locale);
map.keySet().removeAll(keys);
}
/**
* Remove a single entry, if present.
*
* @param category
* @param locale
* @param key
*/
public void removeEntry(String category, GwtLocale locale, String key) {
Map<String, String> map = getMap(category, locale);
map.remove(key);
}
/**
* Reset state, forgetting any cached values.
*/
public void reset() {
maps.clear();
}
/**
* Summarize values known by territories to bare languages, based on
* populations using a particular value.
*
* @param category
* @param key
* @param values map of region codes to values
*/
public void summarizeTerritoryEntries(String category, RegionLanguageData regionLanguageData,
String key, Map<String, String> values) {
for (GwtLocale locale : allLocales.keySet()) {
if (locale.getRegion() != null || locale.getLanguage() == null) {
// skip any that have a region or don't have a language
continue;
}
String language = locale.getAsString();
Map<String, Double> valueMap = new HashMap<String, Double>();
for (RegionLanguageData.RegionPopulation langData : regionLanguageData.getRegions(language)) {
String region = langData.getRegion();
GwtLocale regionLocale = localeFactory.fromString(language + "_" + region);
String value = getTerritoryData(regionLocale, values);
if (value != null) {
Double pop = valueMap.get(value);
if (pop == null) {
pop = 0.0;
}
pop += langData.getLiteratePopulation();
valueMap.put(value, pop);
}
}
double max = 0;
String maxValue = null;
for (Map.Entry<String, Double> entry : valueMap.entrySet()) {
if (entry.getValue() > max) {
max = entry.getValue();
maxValue = entry.getKey();
}
}
if (maxValue != null) {
addEntry(category, locale, key, maxValue);
}
}
// map locales to territory data
for (GwtLocale locale : allLocales.keySet()) {
if (getEntry(category, locale, key) != null) {
// don't override what we set above
continue;
}
String value = getTerritoryData(locale, values);
if (value != null) {
addEntry(category, locale, key, value);
}
}
}
private void addAttributeEntries(String category, InputFactory cldrFactory, String prefix,
String tag, String key, String attribute, String defaultValue) {
for (GwtLocale locale : allLocales.keySet()) {
addAttributeEntry(category, locale, cldrFactory, prefix, tag, key, attribute, defaultValue);
}
}
/**
* Add currency entries for the specified locale. If this locale is not the
* default locale, also add default entries into the default locale to make
* sure it has entries for any currency present in any locale. Note that this
* means that the default locale must be processed last.
*
* @param currencyFractions map of currency fraction data extracted from
* locale-independent data
*/
private void addCurrencyEntries(String category, GwtLocale locale, InputFactory inputFactory,
Map<String, Integer> currencyFractions, int defaultCurrencyFraction, Set<String> stillInUse,
Map<String, Integer> rounding) {
Map<String, String> outputMap = getMap(category, locale);
Map<String, String> defaultMap = null;
if (!locale.isDefault()) {
defaultMap = getMap(category, localeFactory.getDefault());
}
Map<String, Currency> tempMap = new HashMap<String, Currency>();
InputFile file = inputFactory.load(allLocales.get(locale));
XPathParts parts = new XPathParts();
for (String path : file.listPaths("//ldml/numbers/currencies")) {
String fullPath = file.getFullXPath(path);
if (fullPath == null) {
fullPath = path;
}
parts.set(fullPath);
Map<String, String> attr = parts.findAttributes("currency");
if (attr == null) {
continue;
}
String currencyCode = attr.get("type");
Currency currency = tempMap.get(currencyCode);
if (currency == null) {
currency = new Currency(currencyCode);
if (currencyFractions.containsKey(currencyCode)) {
currency.setDecimalDigits(currencyFractions.get(currencyCode));
} else {
currency.setDecimalDigits(defaultCurrencyFraction);
}
currency.setInUse(stillInUse.contains(currencyCode));
tempMap.put(currencyCode, currency);
Integer roundingMult = rounding.get(currencyCode);
if (roundingMult != null) {
currency.setRounding(roundingMult);
}
}
String field = parts.getElement(4);
String value = file.getStringValue(fullPath);
attr = parts.findAttributes(field);
if (attr == null) {
attr = Collections.emptyMap();
}
String draft = attr.get("draft");
if ("symbol".equalsIgnoreCase(field)) {
// Don't overwrite symbol with narrow dollar value
if (currency.getSymbol() == null || !"narrow".equals(parts.getAttributeValue(4, "alt"))) {
currency.setSymbol(value);
}
} else if ("displayName".equalsIgnoreCase(field)) {
if (attr.get("count") != null) {
// We don't care about currency "count" names
continue;
}
if (draft == null || currency.getDisplayName() == null) {
// don't override non-draft name with draft name
currency.setDisplayName(value);
}
} else if ("pattern".equalsIgnoreCase(field)) {
currency.setPattern(value);
} else if ("decimal".equalsIgnoreCase(field)) {
currency.setDecimalSeparator(value);
} else if ("group".equalsIgnoreCase(field)) {
currency.setGroupingSeparator(value);
} else {
System.err.println("Ignoring unknown field \"" + field + "\" on currency data for \""
+ currencyCode + "\"");
}
}
for (Currency currency : tempMap.values()) {
String code = currency.getCode();
outputMap.put(code, encodeCurrencyData(currency));
if (defaultMap != null) {
// Don't copy language-specific things to default
currency.setDisplayName(code);
currency.setSymbol(null);
defaultMap.put(code, encodeCurrencyData(currency));
}
}
}
private void addDateTimeFormatEntries(String group, String length, InputFactory cldrFactory) {
addEntries(group, cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + group
+ "Formats/" + group + "FormatLength" + "[@type=\"" + length + "\"]/" + group
+ "Format[@type=\"standard\"]" + "/pattern[@type=\"standard\"]", group + "FormatLength",
"type");
}
private void buildInheritsFrom() {
for (GwtLocale locale : allLocales.keySet()) {
GwtLocale parent = null;
for (GwtLocale search : locale.getInheritanceChain()) {
if (!search.equals(locale) && allLocales.containsKey(search)) {
parent = search;
break;
}
}
inheritsFrom.put(locale, parent);
}
}
/**
* Build a depth map which is used to sort locales such that more derived
* locales are processed before less derived locales.
*/
private void buildLocaleDepth() {
Set<GwtLocale> remaining = new HashSet<GwtLocale>(allLocales.keySet());
localeDepth.put(defaultLocale, 0);
remaining.remove(defaultLocale);
while (!remaining.isEmpty()) {
Set<GwtLocale> nextPass = new HashSet<GwtLocale>();
for (GwtLocale locale : remaining) {
GwtLocale parent = inheritsFrom.get(locale);
if (localeDepth.containsKey(parent)) {
int depth = localeDepth.get(parent);
localeDepth.put(locale, depth + 1);
} else {
nextPass.add(locale);
}
}
remaining = nextPass;
}
}
/**
* Encode the currency data as needed by CurrencyListGenerator.
*
* @param currency
* @return a string containing the property file entry for the specified
* currency
*/
private String encodeCurrencyData(Currency currency) {
StringBuilder buf = new StringBuilder();
String skipped = "";
String displayName = currency.getDisplayName();
if (displayName == null) {
displayName = currency.getCode();
}
buf.append(displayName);
String symbol = currency.getSymbol();
if (symbol != null && !currency.getCode().equals(symbol)) {
buf.append('|');
buf.append(symbol);
skipped = "";
} else {
skipped = "|";
}
if (currency.getDecimalDigits() != 2) {
buf.append(skipped).append('|');
buf.append(currency.getDecimalDigits());
skipped = "";
} else {
skipped += "|";
}
if (!currency.isInUse()) {
buf.append(skipped).append("|1");
skipped = "";
} else {
skipped += "|";
}
if (currency.getRounding() != 0) {
buf.append(skipped).append("|").append(currency.getRounding());
}
return buf.toString();
}
/**
* Get a map for a given class/locale combination.
*
* @param category
* @param locale
*
* @return map for the specified class/locale
*/
private Map<String, String> getMap(String category, GwtLocale locale) {
MapKey mapKey = new MapKey(category, locale);
Map<String, String> map = maps.get(mapKey);
if (map == null) {
map = new HashMap<String, String>();
maps.put(mapKey, map);
}
return map;
}
/**
* @return an array of map keys, ordered from most derived to least derived.
*/
private MapKey[] getSortedMapKeys() {
Set<MapKey> keySet = maps.keySet();
MapKey[] keys = keySet.toArray(new MapKey[keySet.size()]);
Arrays.sort(keys, new Comparator<MapKey>() {
private final Comparator<GwtLocale> depthComparator = new LocaleComparator();
@Override
public int compare(MapKey a, MapKey b) {
return depthComparator.compare(a.getLocale(), b.getLocale());
}
});
return keys;
}
}