]> granicus.if.org Git - icu/commitdiff
ICU-13285 Adding NumberingSystem constructor methods to DecimalFormatSymbols (J and C).
authorShane Carr <shane@unicode.org>
Thu, 17 Aug 2017 23:49:00 +0000 (23:49 +0000)
committerShane Carr <shane@unicode.org>
Thu, 17 Aug 2017 23:49:00 +0000 (23:49 +0000)
X-SVN-Rev: 40345

icu4c/source/i18n/dcfmtsym.cpp
icu4c/source/i18n/unicode/dcfmtsym.h
icu4c/source/test/intltest/tsdcfmsy.cpp
icu4c/source/test/intltest/tsdcfmsy.h
icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/IntlTestDecimalFormatSymbols.java

index 43eea49ea52d266fc7c04087ccb8b5189654985b..d321a82f8a9550f49eff66b1dbd5a01dbbd2e915 100644 (file)
@@ -97,9 +97,7 @@ static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount]
 // Initializes this with the decimal format symbols in the default locale.
 
 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
-    : UObject(),
-    locale()
-{
+        : UObject(), locale() {
     initialize(locale, status, TRUE);
 }
 
@@ -107,16 +105,17 @@ DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
 // Initializes this with the decimal format symbols in the desired locale.
 
 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
-    : UObject(),
-    locale(loc)
-{
+        : UObject(), locale(loc) {
     initialize(locale, status);
 }
 
+DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSystem& ns, UErrorCode& status)
+        : UObject(), locale(loc) {
+    initialize(locale, status, FALSE, &ns);
+}
+
 DecimalFormatSymbols::DecimalFormatSymbols()
-        : UObject(),
-          locale(Locale::getRoot()),
-          currPattern(NULL) {
+        : UObject(), locale(Locale::getRoot()), currPattern(NULL) {
     *validLocale = *actualLocale = 0;
     initialize();
 }
@@ -342,7 +341,8 @@ CurrencySpacingSink::~CurrencySpacingSink() {}
 } // namespace
 
 void
-DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData)
+DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status,
+    UBool useLastResortData, const NumberingSystem* ns)
 {
     if (U_FAILURE(status)) { return; }
     *validLocale = *actualLocale = 0;
@@ -355,7 +355,13 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool us
     // Next get the numbering system for this locale and set zero digit
     // and the digit string based on the numbering system for the locale
     //
-    LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status));
+    LocalPointer<NumberingSystem> nsLocal;
+    if (ns == nullptr) {
+        // Use the numbering system according to the locale.
+        // Save it into a LocalPointer so it gets cleaned up.
+        nsLocal.adoptInstead(NumberingSystem::createInstance(loc, status));
+        ns = nsLocal.getAlias();
+    }
     const char *nsName;
     if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) {
         nsName = ns->getName();
index 3a502d0ec03d1c7fa3ec308840cd9339518f8757..7ccc21522ede6931afc6dd028cfeeb38e26ee5b6 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "unicode/uobject.h"
 #include "unicode/locid.h"
+#include "unicode/numsys.h"
 #include "unicode/unum.h"
 #include "unicode/unistr.h"
 
@@ -184,6 +185,24 @@ public:
      */
     DecimalFormatSymbols(const Locale& locale, UErrorCode& status);
 
+    /**
+     * Creates a DecimalFormatSymbols instance for the given locale with digits and symbols
+     * corresponding to the given NumberingSystem.
+     *
+     * This constructor behaves equivalently to the normal constructor called with a locale having a
+     * "numbers=xxxx" keyword specifying the numbering system by name.
+     *
+     * In this constructor, the NumberingSystem argument will be used even if the locale has its own
+     * "numbers=xxxx" keyword.
+     *
+     * @param locale    The locale to get symbols for.
+     * @param ns        The numbering system.
+     * @param status    Input/output parameter, set to success or
+     *                  failure code upon return.
+     * @draft ICU 60
+     */
+    DecimalFormatSymbols(const Locale& locale, const NumberingSystem& ns, UErrorCode& status);
+
     /**
      * Create a DecimalFormatSymbols object for the default locale.
      * This constructor will not fail.  If the resource file data is
@@ -346,8 +365,11 @@ private:
      * @param success              Input/output parameter, set to success or
      *                             failure code upon return.
      * @param useLastResortData    determine if use last resort data
+     * @param ns                   The NumberingSystem to use; otherwise, fall
+     *                             back to the locale.
      */
-    void initialize(const Locale& locale, UErrorCode& success, UBool useLastResortData = FALSE);
+    void initialize(const Locale& locale, UErrorCode& success,
+        UBool useLastResortData = FALSE, const NumberingSystem* ns = nullptr);
 
     /**
      * Initialize the symbols with default values.
index eb5bc3bf1ea9acb9117e9cf66fa6de81a2413eff..90198e070f4f513f4191029e08f12eaed581eac1 100644 (file)
@@ -23,6 +23,7 @@ void IntlTestDecimalFormatSymbols::runIndexedTest( int32_t index, UBool exec, co
     TESTCASE_AUTO_BEGIN;
     TESTCASE_AUTO(testSymbols);
     TESTCASE_AUTO(testLastResortData);
+    TESTCASE_AUTO(testNumberingSystem);
     TESTCASE_AUTO_END;
 }
 
@@ -248,6 +249,49 @@ void IntlTestDecimalFormatSymbols::testLastResortData() {
     Verify(1234567.25, "#,##0.##", *lastResort, "1,234,567.25");
 }
 
+void IntlTestDecimalFormatSymbols::testNumberingSystem() {
+    IcuTestErrorCode errorCode(*this, "testNumberingSystem");
+    struct testcase {
+        const char* locid;
+        const char* nsname;
+        const char16_t* expected1; // Expected number format string
+        const char16_t* expected2; // Expected pattern separator
+    };
+    static const testcase cases[9] = {
+            {"en", "latn", u"1,234.56", u";"},
+            {"en", "arab", u"١٬٢٣٤٫٥٦", u"؛"},
+            {"en", "mathsanb", u"𝟭,𝟮𝟯𝟰.𝟱𝟲", u";"},
+            {"en", "mymr", u"၁,၂၃၄.၅၆", u";"},
+            {"my", "latn", u"1,234.56", u";"},
+            {"my", "arab", u"١٬٢٣٤٫٥٦", u"؛"},
+            {"my", "mathsanb", u"𝟭,𝟮𝟯𝟰.𝟱𝟲", u";"},
+            {"my", "mymr", u"၁,၂၃၄.၅၆", u"၊"},
+            {"en@numbers=thai", "mymr", u"၁,၂၃၄.၅၆", u";"}, // conflicting numbering system
+    };
+
+    for (int i=0; i<8; i++) {
+        testcase cas = cases[i];
+        Locale loc(cas.locid);
+        LocalPointer<NumberingSystem> ns(NumberingSystem::createInstanceByName(cas.nsname, errorCode));
+        if (errorCode.logDataIfFailureAndReset("NumberingSystem failed")) {
+            return;
+        }
+        UnicodeString expected1(cas.expected1);
+        UnicodeString expected2(cas.expected2);
+        DecimalFormatSymbols dfs(loc, *ns, errorCode);
+        if (errorCode.logDataIfFailureAndReset("DecimalFormatSymbols failed")) {
+            return;
+        }
+        Verify(1234.56, "#,##0.##", dfs, expected1);
+        // The pattern separator is something that differs by numbering system in my@numbers=mymr.
+        UnicodeString actual2 = dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol);
+        if (expected2 != actual2) {
+            errln((UnicodeString)"ERROR: DecimalFormatSymbols returned pattern separator " + actual2
+                + " but we expected " + expected2);
+        }
+    }
+}
+
 void IntlTestDecimalFormatSymbols::Verify(double value, const UnicodeString& pattern,
                                           const DecimalFormatSymbols &sym, const UnicodeString& expected){
     UErrorCode status = U_ZERO_ERROR;
index 5df1bc7c6fcbb34ab7a23f91bd399e0e6a2146d7..1fd1dfdfba3dc357ecdffe6287d4c5a33bb0826d 100644 (file)
@@ -28,6 +28,7 @@ private:
      */
     void testSymbols(/*char *par*/);
     void testLastResortData();
+    void testNumberingSystem();
 
      /** helper functions**/
     void Verify(double value, const UnicodeString& pattern,
index 8daac03210482e879f3f985de447199e99048b51..147ca20c113a55cc6808d42849b5a3355ea3ae62 100644 (file)
@@ -53,7 +53,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
      * @stable ICU 2.0
      */
     public DecimalFormatSymbols() {
-        initialize(ULocale.getDefault(Category.FORMAT));
+        this(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -62,7 +62,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
      * @stable ICU 2.0
      */
     public DecimalFormatSymbols(Locale locale) {
-        initialize(ULocale.forLocale(locale));
+        this(ULocale.forLocale(locale));
     }
 
     /**
@@ -71,7 +71,15 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
      * @stable ICU 3.2
      */
     public DecimalFormatSymbols(ULocale locale) {
-        initialize(locale);
+        initialize(locale, null);
+    }
+
+    private DecimalFormatSymbols(Locale locale, NumberingSystem ns) {
+        this(ULocale.forLocale(locale), ns);
+    }
+
+    private DecimalFormatSymbols(ULocale locale, NumberingSystem ns) {
+        initialize(locale, ns);
     }
 
     /**
@@ -123,6 +131,46 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
         return new DecimalFormatSymbols(locale);
     }
 
+    /**
+     * {@icu} Returns a DecimalFormatSymbols instance for the given locale with digits and symbols
+     * corresponding to the given {@link NumberingSystem}.
+     *
+     * <p>This method behaves equivalently to {@link #getInstance} called with a locale having a
+     * "numbers=xxxx" keyword specifying the numbering system by name.
+     *
+     * <p>In this method, the NumberingSystem argument will be used even if the locale has its own
+     * "numbers=xxxx" keyword.
+     *
+     * @param locale the locale.
+     * @param ns the numbering system.
+     * @return A DecimalFormatSymbols instance.
+     * @provisional This API might change or be removed in a future release.
+     * @draft ICU 60
+     */
+    public static DecimalFormatSymbols forNumberingSystem(Locale locale, NumberingSystem ns) {
+        return new DecimalFormatSymbols(locale, ns);
+    }
+
+    /**
+     * {@icu} Returns a DecimalFormatSymbols instance for the given locale with digits and symbols
+     * corresponding to the given {@link NumberingSystem}.
+     *
+     * <p>This method behaves equivalently to {@link #getInstance} called with a locale having a
+     * "numbers=xxxx" keyword specifying the numbering system by name.
+     *
+     * <p>In this method, the NumberingSystem argument will be used even if the locale has its own
+     * "numbers=xxxx" keyword.
+     *
+     * @param locale the locale.
+     * @param ns the numbering system.
+     * @return A DecimalFormatSymbols instance.
+     * @provisional This API might change or be removed in a future release.
+     * @draft ICU 60
+     */
+    public static DecimalFormatSymbols forNumberingSystem(ULocale locale, NumberingSystem ns) {
+        return new DecimalFormatSymbols(locale, ns);
+    }
+
     /**
      * Returns an array of all locales for which the <code>getInstance</code> methods of
      * this class can return localized instances.
@@ -1289,10 +1337,16 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
     /**
      * Initializes the symbols from the locale data.
      */
-    private void initialize( ULocale locale ) {
+    private void initialize(ULocale locale, NumberingSystem ns) {
         this.requestedLocale = locale.toLocale();
         this.ulocale = locale;
-        CacheData data = cachedLocaleData.getInstance(locale, null /* unused */);
+
+        // TODO: The cache requires a single key, so we just save the NumberingSystem into the
+        // locale string. NumberingSystem is then decoded again in the loadData() method. It would
+        // be more efficient if we didn't have to serialize and deserialize the NumberingSystem.
+        ULocale keyLocale = (ns == null) ? locale : locale.setKeywordValue("numbers", ns.getName());
+        CacheData data = cachedLocaleData.getInstance(keyLocale, null /* unused */);
+
         setLocale(data.validLocale, data.validLocale);
         setDigitStrings(data.digits);
         String[] numberElements = data.numberElements;
index 2180a789c6f4238364d44231994f924252733204..360f3f2e9043d770c85249427fecc10868c1d49d 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.Test;
 
 import com.ibm.icu.text.DecimalFormat;
 import com.ibm.icu.text.DecimalFormatSymbols;
+import com.ibm.icu.text.NumberingSystem;
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.ULocale;
 
@@ -308,4 +309,40 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
             errln("ERROR: Latin digits should be set" + symbols.getDigitStrings()[0]);
         }
     }
+
+    @Test
+    public void testNumberingSystem() {
+        Object[][] cases = {
+                {"en", "latn", "1,234.56", ';'},
+                {"en", "arab", "١٬٢٣٤٫٥٦", '؛'},
+                {"en", "mathsanb", "𝟭,𝟮𝟯𝟰.𝟱𝟲", ';'},
+                {"en", "mymr", "၁,၂၃၄.၅၆", ';'},
+                {"my", "latn", "1,234.56", ';'},
+                {"my", "arab", "١٬٢٣٤٫٥٦", '؛'},
+                {"my", "mathsanb", "𝟭,𝟮𝟯𝟰.𝟱𝟲", ';'},
+                {"my", "mymr", "၁,၂၃၄.၅၆", '၊'},
+                {"en@numbers=thai", "mymr", "၁,၂၃၄.၅၆", ';'}, // conflicting numbering system
+        };
+
+        for (Object[] cas : cases) {
+            ULocale loc = new ULocale((String) cas[0]);
+            NumberingSystem ns = NumberingSystem.getInstanceByName((String) cas[1]);
+            String expectedFormattedNumberString = (String) cas[2];
+            char expectedPatternSeparator = (Character) cas[3];
+
+            DecimalFormatSymbols dfs = DecimalFormatSymbols.forNumberingSystem(loc, ns);
+            DecimalFormat df = new DecimalFormat("#,##0.##", dfs);
+            String actual1 = df.format(1234.56);
+            assertEquals("1234.56 with " + loc + " and " + ns.getName(),
+                    expectedFormattedNumberString, actual1);
+            // The pattern separator is something that differs by numbering system in my@numbers=mymr.
+            char actual2 = dfs.getPatternSeparator();
+            assertEquals("Pattern separator with " + loc + " and " + ns.getName(),
+                    expectedPatternSeparator, actual2);
+
+            // Coverage for JDK Locale overload
+            DecimalFormatSymbols dfs2 = DecimalFormatSymbols.forNumberingSystem(loc.toLocale(), ns);
+            assertEquals("JDK Locale and ICU Locale should produce the same object", dfs, dfs2);
+        }
+    }
 }