From ac7474e79f853ad162bc41fd242324df306e5180 Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Fri, 29 Apr 2016 23:22:17 +0000 Subject: [PATCH] ICU-12521 Updating DecimalFormatSymbols to data sink X-SVN-Rev: 38676 --- .../ibm/icu/text/DecimalFormatSymbols.java | 189 +++++++++++++----- 1 file changed, 134 insertions(+), 55 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java index 1ed689ab1fb..13ac7795a79 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java @@ -24,6 +24,7 @@ import com.ibm.icu.util.Currency; import com.ibm.icu.util.ICUCloneNotSupportedException; import com.ibm.icu.util.ULocale; import com.ibm.icu.util.ULocale.Category; +import com.ibm.icu.impl.UResource; import com.ibm.icu.util.UResourceBundle; /** @@ -840,6 +841,84 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { return (c=='\u200E' || c=='\u200F' || c=='\u061C'); } + /** + * List of field names to be loaded from the data files. + * The indices of each name into the array correspond to the position of that item in the + * numberElements array. + */ + private static final String[] SYMBOL_KEYS = { + "decimal", + "group", + "list", + "percentSign", + "minusSign", + "plusSign", + "exponential", + "perMille", + "infinity", + "nan", + "currencyDecimal", + "currencyGroup", + "superscriptingExponent" + }; + + /** + * List of default values for the symbols. + */ + private static final String[] SYMBOL_DEFAULTS = new String[] { + ".", // decimal + ",", // group + ";", // list + "%", // percentSign + "-", // minusSign + "+", // plusSign + "E", // exponential + "\u2030", // perMille + "\u221e", // infinity + "NaN", // NaN + null, // currency decimal + null, // currency group + "\u00D7" // superscripting exponent + }; + + /** + * Constants for path names in the data bundles. + */ + private static final String LATIN_NUMBERING_SYSTEM = "latn"; + private static final String NUMBER_ELEMENTS = "NumberElements"; + private static final String SYMBOLS = "symbols"; + + /** + * Sink for enumerating all of the decimal format symbols (more specifically, anything + * under the "NumberElements.symbols" tree). + * + * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): + * Only store a value if it is still missing, that is, it has not been overridden. + */ + private static final class DecFmtDataSink extends UResource.Sink { + + private String[] numberElements; // Array where to store the characters (set in constructor) + + public DecFmtDataSink(String[] numberElements) { + this.numberElements = numberElements; + } + + @Override + public void put(UResource.Key key, UResource.Value value, boolean noFallback) { + UResource.Table symbolsTable = value.getTable(); + for (int j = 0; symbolsTable.getKeyAndValue(j, key, value); ++j) { + for (int i = 0; i < SYMBOL_KEYS.length; i++) { + if (key.contentEquals(SYMBOL_KEYS[i])) { + if (numberElements[i] == null) { + numberElements[i] = value.toString(); + } + break; + } + } + } + } + } + /** * Initializes the symbols from the LocaleElements resource bundle. * Note: The organization of LocaleElements badly needs to be @@ -878,52 +957,67 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { digits[7] = DecimalFormat.PATTERN_SEVEN_DIGIT; digits[8] = DecimalFormat.PATTERN_EIGHT_DIGIT; digits[9] = DecimalFormat.PATTERN_NINE_DIGIT; - nsName = "latn"; // Default numbering system + nsName = LATIN_NUMBERING_SYSTEM; // Default numbering system } - /* try the cache first */ + // Open the resource bundle and get the locale IDs + // TODO: Is there a better way to get the locale than making an ICUResourceBundle instance? + ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle. + getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale); + // TODO: Determine actual and valid locale correctly. + ULocale uloc = rb.getULocale(); + setLocale(uloc, uloc); + + // Try loading from the cache String[][] data = cachedLocaleData.get(locale); - String[] numberElements; - if (data == null) { /* cache miss */ - data = new String[1][]; - ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle. - getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale); - boolean isLatn = nsName.equals("latn"); - String baseKey = "NumberElements/" + nsName + "/symbols/"; - String latnKey = "NumberElements/latn/symbols/"; - String[] symbolKeys = { "decimal", "group", "list", "percentSign", "minusSign", "plusSign", "exponential", "perMille", "infinity", "nan", "currencyDecimal", "currencyGroup", "superscriptingExponent" }; - String[] fallbackElements = { ".", ",", ";", "%", "-", "+", "E", "\u2030", "\u221e", "NaN", null, null }; - String[] symbolsArray = new String[symbolKeys.length]; - for ( int i = 0 ; i < symbolKeys.length; i++ ) { - try { - symbolsArray[i] = rb.getStringWithFallback(baseKey+symbolKeys[i]); - } catch (MissingResourceException ex) { - if (!isLatn) { // Fall back to latn numbering system for symbols if desired symbol isn't found. - try { - symbolsArray[i] = rb.getStringWithFallback(latnKey+symbolKeys[i]); - } catch (MissingResourceException ex1) { - symbolsArray[i] = fallbackElements[i]; - } - } else { - symbolsArray[i] = fallbackElements[i]; - } + if (data == null) { + // Cache miss! + // TODO: There does not appear to be a good reason why the "data" array is 2-D. + data = new String[1][SYMBOL_KEYS.length]; + + // Load using a data sink + DecFmtDataSink sink = new DecFmtDataSink(data[0]); + try { + rb.getAllItemsWithFallback(NUMBER_ELEMENTS + "/" + nsName + "/" + SYMBOLS, sink); + } catch (MissingResourceException e) { + // The symbols don't exist for the given nsName and resource bundle. + // Silently ignore and fall back to Latin. + } + + // Load the Latin fallback if necessary + boolean hasNull = false; + for (String entry : data[0]) { + if (entry == null) { + hasNull = true; + break; + } + } + if (hasNull && !nsName.equals(LATIN_NUMBERING_SYSTEM)) { + rb.getAllItemsWithFallback(NUMBER_ELEMENTS + "/" + LATIN_NUMBERING_SYSTEM + "/" + SYMBOLS, sink); + } + + // If monetary decimal or grouping were not explicitly set, then set them to be the same as + // their non-monetary counterparts. + if (data[0][10] == null) { + data[0][10] = data[0][0]; + } + if (data[0][11] == null) { + data[0][11] = data[0][1]; + } + + // Fill in any remaining missing values + for (int i = 0; i < SYMBOL_KEYS.length; i++) { + if (data[0][i] == null) { + data[0][i] = SYMBOL_DEFAULTS[i]; } } - data[0] = symbolsArray; - /* update cache */ + // Save in cache cachedLocaleData.put(locale, data); } - numberElements = data[0]; - - ICUResourceBundle r = (ICUResourceBundle)UResourceBundle. - getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale); - - // TODO: Determine actual and valid locale correctly. - ULocale uloc = r.getULocale(); - setLocale(uloc, uloc); - + String[] numberElements = data[0]; + // Copy data from the numberElements map into instance fields decimalSeparator = numberElements[0].charAt(0); groupingSeparator = numberElements[1].charAt(0); patternSeparator = numberElements[2].charAt(0); @@ -936,25 +1030,10 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { perMill = numberElements[7].charAt(0); infinity = numberElements[8]; NaN = numberElements[9]; + monetarySeparator = numberElements[10].charAt(0); + monetaryGroupingSeparator = numberElements[11].charAt(0); + exponentMultiplicationSign = numberElements[12]; - if ( numberElements[10] != null) { - monetarySeparator = numberElements[10].charAt(0); - } else { - monetarySeparator = decimalSeparator; - } - - if ( numberElements[11] != null) { - monetaryGroupingSeparator = numberElements[11].charAt(0); - } else { - monetaryGroupingSeparator = groupingSeparator; - } - - if ( numberElements[12] != null) { - exponentMultiplicationSign = numberElements[12]; - } else { - exponentMultiplicationSign = "\u00D7"; - } - digit = DecimalFormat.PATTERN_DIGIT; // Localized pattern character no longer in CLDR padEscape = DecimalFormat.PATTERN_PAD_ESCAPE; sigDigit = DecimalFormat.PATTERN_SIGNIFICANT_DIGIT;