]> granicus.if.org Git - icu/commitdiff
ICU-12029 Measure unit display names, C++ version.
authorShane Carr <shane@unicode.org>
Thu, 15 Sep 2016 08:37:56 +0000 (08:37 +0000)
committerShane Carr <shane@unicode.org>
Thu, 15 Sep 2016 08:37:56 +0000 (08:37 +0000)
X-SVN-Rev: 39243

icu4c/source/i18n/measfmt.cpp
icu4c/source/i18n/unicode/measfmt.h
icu4c/source/test/intltest/measfmttest.cpp

index 4ad5a29564b098dc80dfff9f943fc446772e8db6..61618ab44d0c86b7d1fa894e2d04498da6e2ff0a 100644 (file)
@@ -110,6 +110,7 @@ public:
     UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT];
     /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
     SimpleFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT];
+    const UChar* dnams[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
     SimpleFormatter perFormatters[WIDTH_INDEX_COUNT];
 
     MeasureFormatCacheData();
@@ -159,6 +160,7 @@ MeasureFormatCacheData::MeasureFormatCacheData() {
         currencyFormats[i] = NULL;
     }
     uprv_memset(patterns, 0, sizeof(patterns));
+    uprv_memset(dnams, 0, sizeof(dnams));
     integerFormat = NULL;
     numericDateFormatters = NULL;
 }
@@ -174,6 +176,7 @@ MeasureFormatCacheData::~MeasureFormatCacheData() {
             }
         }
     }
+    // Note: the contents of 'dnams' are pointers into the resource bundle
     delete integerFormat;
     delete numericDateFormatters;
 }
@@ -232,17 +235,25 @@ struct UnitDataSink : public ResourceSink {
 
     void setFormatterIfAbsent(int32_t index, const ResourceValue &value,
                                 int32_t minPlaceholders, UErrorCode &errorCode) {
-        SimpleFormatter **patterns =
-            &cacheData.patterns[unitIndex][width][0];
+        SimpleFormatter **patterns = &cacheData.patterns[unitIndex][width][0];
         if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
-            patterns[index] = new SimpleFormatter(
-                    value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode);
+            if (minPlaceholders >= 0) {
+                patterns[index] = new SimpleFormatter(
+                        value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode);
+            }
             if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
                 errorCode = U_MEMORY_ALLOCATION_ERROR;
             }
         }
     }
 
+    void setDnamIfAbsent(const ResourceValue &value, UErrorCode& errorCode) {
+        if (cacheData.dnams[unitIndex][width] == NULL) {
+            int32_t length;
+            cacheData.dnams[unitIndex][width] = value.getString(length, errorCode);
+        }
+    }
+
     /**
      * Consume a display pattern. For example,
      * unitsShort/duration/hour contains other{"{0} hrs"}.
@@ -250,7 +261,8 @@ struct UnitDataSink : public ResourceSink {
     void consumePattern(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
         if (U_FAILURE(errorCode)) { return; }
         if (uprv_strcmp(key, "dnam") == 0) {
-            // Skip the unit display name for now.
+            // The display name for the unit in the current width.
+            setDnamIfAbsent(value, errorCode);
         } else if (uprv_strcmp(key, "per") == 0) {
             // For example, "{0}/h".
             setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, value, 1, errorCode);
@@ -817,6 +829,24 @@ UnicodeString &MeasureFormat::formatMeasures(
     return appendTo;
 }
 
+UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& /*status*/) const {
+    UMeasureFormatWidth width = getRegularWidth(this->width);
+    const UChar* const* styleToDnam = cache->dnams[unit.getIndex()];
+    const UChar* dnam = styleToDnam[width];
+    if (dnam == NULL) {
+        int32_t fallbackWidth = cache->widthFallback[width];
+        dnam = styleToDnam[fallbackWidth];
+    }
+
+    UnicodeString result;
+    if (dnam == NULL) {
+        result.setToBogus();
+    } else {
+        result.setTo(dnam, -1);
+    }
+    return result;
+}
+
 void MeasureFormat::initMeasureFormat(
         const Locale &locale,
         UMeasureFormatWidth w,
index a6d8cb81b5e295e12195df92713ab9c00b762bb3..de545f160137addc07bd916fbcd98ef595690853 100644 (file)
@@ -210,6 +210,19 @@ class U_I18N_API MeasureFormat : public Format {
             FieldPosition &pos,
             UErrorCode &status) const;
 
+    /**
+     * Gets the display name of the specified {@link MeasureUnit} corresponding to the current
+     * locale and format width.
+     * @param unit  The unit for which to get a display name.
+     * @param status the error.
+     * @return  The display name in the locale and width specified in
+     *          {@link MeasureFormat#getInstance}, or null if there is no display name available
+     *          for the specified unit.
+     *
+     * @draft ICU 58
+     */
+    UnicodeString getUnitDisplayName(const MeasureUnit& unit, UErrorCode &status) const;
+
 
     /**
      * Return a formatter for CurrencyAmount objects in the given
index 4728bc4bb3d828d6f5b707372b4f53f7a4d767be..7bd0b52cfedd8398cfaa11c3f9f05e204b1ce20a 100644 (file)
@@ -24,6 +24,7 @@
 #include "unicode/tmunit.h"
 #include "unicode/plurrule.h"
 #include "charstr.h"
+#include "cstr.h"
 #include "unicode/reldatefmt.h"
 
 struct ExpectedResult {
@@ -58,6 +59,7 @@ private:
     void TestManyLocaleDurations();
     void TestGram();
     void TestCurrencies();
+    void TestDisplayNames();
     void TestFieldPosition();
     void TestFieldPositionMultiple();
     void TestBadArg();
@@ -111,6 +113,11 @@ private:
         const Measure *measures,
         int32_t measureCount,
         const char *expected);
+    void helperTestDisplayName(
+        const MeasureUnit *unit,
+        const char *localeID,
+        UMeasureFormatWidth width,
+        const char *expected);
     void verifyFieldPosition(
         const char *description,
         const MeasureFormat &fmt,
@@ -147,6 +154,7 @@ void MeasureFormatTest::runIndexedTest(
     TESTCASE_AUTO(TestManyLocaleDurations);
     TESTCASE_AUTO(TestGram);
     TESTCASE_AUTO(TestCurrencies);
+    TESTCASE_AUTO(TestDisplayNames);
     TESTCASE_AUTO(TestFieldPosition);
     TESTCASE_AUTO(TestFieldPositionMultiple);
     TESTCASE_AUTO(TestBadArg);
@@ -1592,6 +1600,61 @@ void MeasureFormatTest::TestCurrencies() {
     verifyFormat("TestCurrenciesNumeric", fmt, &USD_2, 1, "$2.00");
 }
 
+void MeasureFormatTest::TestDisplayNames() {
+    UErrorCode status = U_ZERO_ERROR;
+    helperTestDisplayName( MeasureUnit::createYear(status), "en", UMEASFMT_WIDTH_WIDE, "years" );
+    helperTestDisplayName( MeasureUnit::createYear(status), "ja", UMEASFMT_WIDTH_WIDE, "\\u5E74" );
+    helperTestDisplayName( MeasureUnit::createYear(status), "es", UMEASFMT_WIDTH_WIDE, "a\\u00F1os" );
+    helperTestDisplayName( MeasureUnit::createYear(status), "pt", UMEASFMT_WIDTH_WIDE, "anos" );
+    helperTestDisplayName( MeasureUnit::createYear(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "anos" );
+    helperTestDisplayName( MeasureUnit::createAmpere(status), "en", UMEASFMT_WIDTH_WIDE, "amperes" );
+    helperTestDisplayName( MeasureUnit::createAmpere(status), "ja", UMEASFMT_WIDTH_WIDE, "\\u30A2\\u30F3\\u30DA\\u30A2" );
+    helperTestDisplayName( MeasureUnit::createAmpere(status), "es", UMEASFMT_WIDTH_WIDE, "amperios" );
+    helperTestDisplayName( MeasureUnit::createAmpere(status), "pt", UMEASFMT_WIDTH_WIDE, "amperes" );
+    helperTestDisplayName( MeasureUnit::createAmpere(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "amperes" );
+    helperTestDisplayName( MeasureUnit::createMeterPerSecondSquared(status), "pt", UMEASFMT_WIDTH_WIDE, "metros por segundo ao quadrado" );
+    helperTestDisplayName( MeasureUnit::createMeterPerSecondSquared(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "metros por segundo quadrado" );
+    helperTestDisplayName( MeasureUnit::createSquareKilometer(status), "pt", UMEASFMT_WIDTH_NARROW, "km\\u00B2" );
+    helperTestDisplayName( MeasureUnit::createSquareKilometer(status), "pt", UMEASFMT_WIDTH_SHORT, "km\\u00B2" );
+    helperTestDisplayName( MeasureUnit::createSquareKilometer(status), "pt", UMEASFMT_WIDTH_WIDE, "quil\\u00F4metros quadrados" );
+    helperTestDisplayName( MeasureUnit::createSecond(status), "pt-PT", UMEASFMT_WIDTH_NARROW, "s" );
+    helperTestDisplayName( MeasureUnit::createSecond(status), "pt-PT", UMEASFMT_WIDTH_SHORT, "s" );
+    helperTestDisplayName( MeasureUnit::createSecond(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "segundos" );
+    helperTestDisplayName( MeasureUnit::createSecond(status), "pt", UMEASFMT_WIDTH_NARROW, "seg" );
+    helperTestDisplayName( MeasureUnit::createSecond(status), "pt", UMEASFMT_WIDTH_SHORT, "segs" );
+    helperTestDisplayName( MeasureUnit::createSecond(status), "pt", UMEASFMT_WIDTH_WIDE, "segundos" );
+    assertSuccess("Error creating measure units", status);
+}
+
+void MeasureFormatTest::helperTestDisplayName(const MeasureUnit *unit,
+                            const char *localeID,
+                            UMeasureFormatWidth width,
+                            const char *expected) {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt(Locale(localeID), width, status);
+    if (U_FAILURE(status)) {
+        errln("Could not create MeasureFormat for locale %s, width %d, status: %s",
+            localeID, (int)width, u_errorName(status));
+        return;
+    }
+
+    UnicodeString dnam = fmt.getUnitDisplayName(*unit, status);
+    if (U_FAILURE(status)) {
+        errln("MeasureFormat::getUnitDisplayName failed for unit %s-%s, locale %s, width %d, status: %s",
+            unit->getType(), unit->getSubtype(), localeID, (int)width, u_errorName(status));
+        return;
+    }
+
+    UnicodeString expStr(UnicodeString(expected).unescape());
+    if (dnam != expStr) {
+        errln("MeasureFormat::getUnitDisplayName for unit %s-%s, locale %s, width %d: expected \"%s\", got \"%s\"",
+            unit->getType(), unit->getSubtype(), localeID, (int)width, CStr(expStr)(), CStr(dnam)());
+    }
+
+    // Delete the measure unit
+    delete unit;
+}
+
 void MeasureFormatTest::TestFieldPosition() {
     UErrorCode status = U_ZERO_ERROR;
     MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);