| Index: source/i18n/measfmt.cpp
|
| diff --git a/source/i18n/measfmt.cpp b/source/i18n/measfmt.cpp
|
| index 6f8851c80679f3bab1168aba33dc816e09ca176d..1af72e9da8d2344a1828ecf3862c37b1e3ec7a52 100644
|
| --- a/source/i18n/measfmt.cpp
|
| +++ b/source/i18n/measfmt.cpp
|
| @@ -17,6 +17,7 @@
|
| #include "unicode/numfmt.h"
|
| #include "currfmt.h"
|
| #include "unicode/localpointer.h"
|
| +#include "resource.h"
|
| #include "simplepatternformatter.h"
|
| #include "quantityformatter.h"
|
| #include "unicode/plurrule.h"
|
| @@ -35,6 +36,7 @@
|
|
|
| #include "sharednumberformat.h"
|
| #include "sharedpluralrules.h"
|
| +#include "standardplural.h"
|
| #include "unifiedcache.h"
|
|
|
| #define MEAS_UNIT_COUNT 129
|
| @@ -76,20 +78,53 @@ private:
|
| NumericDateFormatters &operator=(const NumericDateFormatters &other);
|
| };
|
|
|
| -// Instances contain all MeasureFormat specific data for a particular locale.
|
| -// This data is cached. It is never copied, but is shared via shared pointers.
|
| +static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) {
|
| + if (width >= WIDTH_INDEX_COUNT) {
|
| + return UMEASFMT_WIDTH_NARROW;
|
| + }
|
| + return width;
|
| +}
|
| +
|
| +/**
|
| + * Instances contain all MeasureFormat specific data for a particular locale.
|
| + * This data is cached. It is never copied, but is shared via shared pointers.
|
| + *
|
| + * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
|
| + * complete sets of unit & per patterns,
|
| + * to correspond to the resource data and its aliases.
|
| + *
|
| + * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
|
| + */
|
| class MeasureFormatCacheData : public SharedObject {
|
| public:
|
| - QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
|
| + static const int32_t PER_UNIT_INDEX = StandardPlural::COUNT;
|
| + static const int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1;
|
| +
|
| + /**
|
| + * Redirection data from root-bundle, top-level sideways aliases.
|
| + * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
|
| + * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
|
| + */
|
| + UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT];
|
| + /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
|
| + SimplePatternFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT];
|
| SimplePatternFormatter perFormatters[WIDTH_INDEX_COUNT];
|
|
|
| MeasureFormatCacheData();
|
| + virtual ~MeasureFormatCacheData();
|
| +
|
| + UBool hasPerFormatter(int32_t width) const {
|
| + // TODO: Create a more obvious way to test if the per-formatter has been set?
|
| + // Use pointers, check for NULL? Or add an isValid() method?
|
| + return perFormatters[width].getPlaceholderCount() == 2;
|
| + }
|
| +
|
| void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
|
| delete currencyFormats[widthIndex];
|
| currencyFormats[widthIndex] = nfToAdopt;
|
| }
|
| - const NumberFormat *getCurrencyFormat(int32_t widthIndex) const {
|
| - return currencyFormats[widthIndex];
|
| + const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const {
|
| + return currencyFormats[getRegularWidth(width)];
|
| }
|
| void adoptIntegerFormat(NumberFormat *nfToAdopt) {
|
| delete integerFormat;
|
| @@ -105,36 +140,23 @@ public:
|
| const NumericDateFormatters *getNumericDateFormatters() const {
|
| return numericDateFormatters;
|
| }
|
| - void adoptPerUnitFormatter(
|
| - int32_t index,
|
| - int32_t widthIndex,
|
| - SimplePatternFormatter *formatterToAdopt) {
|
| - delete perUnitFormatters[index][widthIndex];
|
| - perUnitFormatters[index][widthIndex] = formatterToAdopt;
|
| - }
|
| - const SimplePatternFormatter * const * getPerUnitFormattersByIndex(
|
| - int32_t index) const {
|
| - return perUnitFormatters[index];
|
| - }
|
| - virtual ~MeasureFormatCacheData();
|
| +
|
| private:
|
| NumberFormat *currencyFormats[WIDTH_INDEX_COUNT];
|
| NumberFormat *integerFormat;
|
| NumericDateFormatters *numericDateFormatters;
|
| - SimplePatternFormatter *perUnitFormatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
|
| MeasureFormatCacheData(const MeasureFormatCacheData &other);
|
| MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
|
| };
|
|
|
| MeasureFormatCacheData::MeasureFormatCacheData() {
|
| + for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
|
| + widthFallback[i] = UMEASFMT_WIDTH_COUNT;
|
| + }
|
| for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
|
| currencyFormats[i] = NULL;
|
| }
|
| - for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
|
| - for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
|
| - perUnitFormatters[i][j] = NULL;
|
| - }
|
| - }
|
| + uprv_memset(patterns, 0, sizeof(patterns));
|
| integerFormat = NULL;
|
| numericDateFormatters = NULL;
|
| }
|
| @@ -145,20 +167,15 @@ MeasureFormatCacheData::~MeasureFormatCacheData() {
|
| }
|
| for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
|
| for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
|
| - delete perUnitFormatters[i][j];
|
| + for (int32_t k = 0; k < PATTERN_COUNT; ++k) {
|
| + delete patterns[i][j][k];
|
| + }
|
| }
|
| }
|
| delete integerFormat;
|
| delete numericDateFormatters;
|
| }
|
|
|
| -static int32_t widthToIndex(UMeasureFormatWidth width) {
|
| - if (width >= WIDTH_INDEX_COUNT) {
|
| - return WIDTH_INDEX_COUNT - 1;
|
| - }
|
| - return width;
|
| -}
|
| -
|
| static UBool isCurrency(const MeasureUnit &unit) {
|
| return (uprv_strcmp(unit.getType(), "currency") == 0);
|
| }
|
| @@ -176,118 +193,219 @@ static UBool getString(
|
| return TRUE;
|
| }
|
|
|
| +namespace {
|
|
|
| -static UBool loadMeasureUnitData(
|
| - const UResourceBundle *resource,
|
| - MeasureFormatCacheData &cacheData,
|
| - UErrorCode &status) {
|
| - if (U_FAILURE(status)) {
|
| - return FALSE;
|
| - }
|
| - static const char *widthPath[] = {"units", "unitsShort", "unitsNarrow"};
|
| - MeasureUnit *units = NULL;
|
| - int32_t unitCount = MeasureUnit::getAvailable(units, 0, status);
|
| - while (status == U_BUFFER_OVERFLOW_ERROR) {
|
| - status = U_ZERO_ERROR;
|
| - delete [] units;
|
| - units = new MeasureUnit[unitCount];
|
| - if (units == NULL) {
|
| - status = U_MEMORY_ALLOCATION_ERROR;
|
| - return FALSE;
|
| - }
|
| - unitCount = MeasureUnit::getAvailable(units, unitCount, status);
|
| - }
|
| - for (int32_t currentWidth = 0; currentWidth < WIDTH_INDEX_COUNT; ++currentWidth) {
|
| - // Be sure status is clear since next resource bundle lookup may fail.
|
| - if (U_FAILURE(status)) {
|
| - delete [] units;
|
| - return FALSE;
|
| - }
|
| - LocalUResourceBundlePointer widthBundle(
|
| - ures_getByKeyWithFallback(
|
| - resource, widthPath[currentWidth], NULL, &status));
|
| - // We may not have data for all widths in all locales.
|
| - if (status == U_MISSING_RESOURCE_ERROR) {
|
| - status = U_ZERO_ERROR;
|
| - continue;
|
| +static const UChar g_LOCALE_units[] = {
|
| + 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
|
| + 0x75, 0x6E, 0x69, 0x74, 0x73
|
| +};
|
| +static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
|
| +static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 };
|
| +
|
| +/**
|
| + * Sink for enumerating all of the measurement unit display names.
|
| + * Contains inner sink classes, each one corresponding to a type of resource table.
|
| + * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables.
|
| + *
|
| + * 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.
|
| + *
|
| + * C++: Each inner sink class has a reference to the main outer sink.
|
| + * Java: Use non-static inner classes instead.
|
| + */
|
| +struct UnitDataSink : public ResourceTableSink {
|
| + /**
|
| + * Sink for a table of display patterns. For example,
|
| + * unitsShort/duration/hour contains other{"{0} hrs"}.
|
| + */
|
| + struct UnitPatternSink : public ResourceTableSink {
|
| + UnitPatternSink(UnitDataSink &sink) : outer(sink) {}
|
| + ~UnitPatternSink();
|
| +
|
| + void setFormatterIfAbsent(int32_t index, const ResourceValue &value,
|
| + int32_t minPlaceholders, UErrorCode &errorCode) {
|
| + SimplePatternFormatter **patterns =
|
| + &outer.cacheData.patterns[outer.unitIndex][outer.width][0];
|
| + if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
|
| + patterns[index] = new SimplePatternFormatter(
|
| + value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode);
|
| + if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
|
| + errorCode = U_MEMORY_ALLOCATION_ERROR;
|
| + }
|
| + }
|
| }
|
| - {
|
| - // compound per
|
| - LocalUResourceBundlePointer compoundPerBundle(
|
| - ures_getByKeyWithFallback(
|
| - widthBundle.getAlias(),
|
| - "compound/per",
|
| - NULL,
|
| - &status));
|
| - if (U_FAILURE(status)) {
|
| - status = U_ZERO_ERROR;
|
| +
|
| + virtual void put(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.
|
| + } else if (uprv_strcmp(key, "per") == 0) {
|
| + // For example, "{0}/h".
|
| + setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, value, 1, errorCode);
|
| } else {
|
| - UnicodeString perPattern;
|
| - getString(compoundPerBundle.getAlias(), perPattern, status);
|
| - cacheData.perFormatters[currentWidth].compile(perPattern, status);
|
| + // The key must be one of the plural form strings. For example:
|
| + // one{"{0} hr"}
|
| + // other{"{0} hrs"}
|
| + setFormatterIfAbsent(StandardPlural::indexFromString(key, errorCode), value, 0,
|
| + errorCode);
|
| }
|
| }
|
| - for (int32_t currentUnit = 0; currentUnit < unitCount; ++currentUnit) {
|
| - // Be sure status is clear next lookup may fail.
|
| - if (U_FAILURE(status)) {
|
| - delete [] units;
|
| - return FALSE;
|
| + UnitDataSink &outer;
|
| + } patternSink;
|
| +
|
| + /**
|
| + * Sink for a table of per-unit tables. For example,
|
| + * unitsShort/duration contains tables for duration-unit subtypes day & hour.
|
| + */
|
| + struct UnitSubtypeSink : public ResourceTableSink {
|
| + UnitSubtypeSink(UnitDataSink &sink) : outer(sink) {}
|
| + ~UnitSubtypeSink();
|
| + virtual ResourceTableSink *getOrCreateTableSink(
|
| + const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
|
| + if (U_FAILURE(errorCode)) { return NULL; }
|
| + outer.unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(outer.type, key);
|
| + if (outer.unitIndex >= 0) {
|
| + return &outer.patternSink;
|
| }
|
| - if (isCurrency(units[currentUnit])) {
|
| - continue;
|
| + return NULL;
|
| + }
|
| + UnitDataSink &outer;
|
| + } subtypeSink;
|
| +
|
| + /**
|
| + * Sink for compound x-per-y display pattern. For example,
|
| + * unitsShort/compound/per may be "{0}/{1}".
|
| + */
|
| + struct UnitCompoundSink : public ResourceTableSink {
|
| + UnitCompoundSink(UnitDataSink &sink) : outer(sink) {}
|
| + ~UnitCompoundSink();
|
| + virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
|
| + if (U_SUCCESS(errorCode) && uprv_strcmp(key, "per") == 0) {
|
| + outer.cacheData.perFormatters[outer.width].
|
| + compileMinMaxPlaceholders(value.getUnicodeString(errorCode), 2, 2, errorCode);
|
| }
|
| - CharString pathBuffer;
|
| - pathBuffer.append(units[currentUnit].getType(), status)
|
| - .append("/", status)
|
| - .append(units[currentUnit].getSubtype(), status);
|
| - LocalUResourceBundlePointer unitBundle(
|
| - ures_getByKeyWithFallback(
|
| - widthBundle.getAlias(),
|
| - pathBuffer.data(),
|
| - NULL,
|
| - &status));
|
| - // We may not have data for all units in all widths
|
| - if (status == U_MISSING_RESOURCE_ERROR) {
|
| - status = U_ZERO_ERROR;
|
| - continue;
|
| + }
|
| + UnitDataSink &outer;
|
| + } compoundSink;
|
| +
|
| + /**
|
| + * Sink for a table of unit type tables. For example,
|
| + * unitsShort contains tables for area & duration.
|
| + * It also contains a table for the compound/per pattern.
|
| + */
|
| + struct UnitTypeSink : public ResourceTableSink {
|
| + UnitTypeSink(UnitDataSink &sink) : outer(sink) {}
|
| + ~UnitTypeSink();
|
| + virtual ResourceTableSink *getOrCreateTableSink(
|
| + const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
|
| + if (U_FAILURE(errorCode)) { return NULL; }
|
| + if (uprv_strcmp(key, "currency") == 0) {
|
| + // Skip.
|
| + } else if (uprv_strcmp(key, "compound") == 0) {
|
| + if (!outer.cacheData.hasPerFormatter(outer.width)) {
|
| + return &outer.compoundSink;
|
| + }
|
| + } else {
|
| + outer.type = key;
|
| + return &outer.subtypeSink;
|
| }
|
| - // We must have the unit bundle to proceed
|
| - if (U_FAILURE(status)) {
|
| - delete [] units;
|
| - return FALSE;
|
| + return NULL;
|
| + }
|
| + UnitDataSink &outer;
|
| + } typeSink;
|
| +
|
| + UnitDataSink(MeasureFormatCacheData &outputData)
|
| + : patternSink(*this), subtypeSink(*this), compoundSink(*this), typeSink(*this),
|
| + cacheData(outputData),
|
| + width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {}
|
| + ~UnitDataSink();
|
| + virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
|
| + // Handle aliases like
|
| + // units:alias{"/LOCALE/unitsShort"}
|
| + // which should only occur in the root bundle.
|
| + if (U_FAILURE(errorCode) || value.getType() != URES_ALIAS) { return; }
|
| + UMeasureFormatWidth sourceWidth = widthFromKey(key);
|
| + if (sourceWidth == UMEASFMT_WIDTH_COUNT) {
|
| + // Alias from something we don't care about.
|
| + return;
|
| + }
|
| + UMeasureFormatWidth targetWidth = widthFromAlias(value, errorCode);
|
| + if (targetWidth == UMEASFMT_WIDTH_COUNT) {
|
| + // We do not recognize what to fall back to.
|
| + errorCode = U_INVALID_FORMAT_ERROR;
|
| + return;
|
| + }
|
| + // Check that we do not fall back to another fallback.
|
| + if (cacheData.widthFallback[targetWidth] != UMEASFMT_WIDTH_COUNT) {
|
| + errorCode = U_INVALID_FORMAT_ERROR;
|
| + return;
|
| + }
|
| + cacheData.widthFallback[sourceWidth] = targetWidth;
|
| + }
|
| + virtual ResourceTableSink *getOrCreateTableSink(
|
| + const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
|
| + if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) {
|
| + return &typeSink;
|
| + }
|
| + return NULL;
|
| + }
|
| +
|
| + static UMeasureFormatWidth widthFromKey(const char *key) {
|
| + if (uprv_strncmp(key, "units", 5) == 0) {
|
| + key += 5;
|
| + if (*key == 0) {
|
| + return UMEASFMT_WIDTH_WIDE;
|
| + } else if (uprv_strcmp(key, "Short") == 0) {
|
| + return UMEASFMT_WIDTH_SHORT;
|
| + } else if (uprv_strcmp(key, "Narrow") == 0) {
|
| + return UMEASFMT_WIDTH_NARROW;
|
| }
|
| - int32_t size = ures_getSize(unitBundle.getAlias());
|
| - for (int32_t plIndex = 0; plIndex < size; ++plIndex) {
|
| - LocalUResourceBundlePointer pluralBundle(
|
| - ures_getByIndex(
|
| - unitBundle.getAlias(), plIndex, NULL, &status));
|
| - if (U_FAILURE(status)) {
|
| - delete [] units;
|
| - return FALSE;
|
| - }
|
| - const char * resKey = ures_getKey(pluralBundle.getAlias());
|
| - if (uprv_strcmp(resKey, "dnam") == 0) {
|
| - continue; // skip display name & per pattern (new in CLDR 26 / ICU 54) for now, not part of plurals
|
| - }
|
| - if (uprv_strcmp(resKey, "per") == 0) {
|
| - UnicodeString perPattern;
|
| - getString(pluralBundle.getAlias(), perPattern, status);
|
| - cacheData.adoptPerUnitFormatter(
|
| - units[currentUnit].getIndex(),
|
| - currentWidth,
|
| - new SimplePatternFormatter(perPattern));
|
| - continue;
|
| - }
|
| - UnicodeString rawPattern;
|
| - getString(pluralBundle.getAlias(), rawPattern, status);
|
| - cacheData.formatters[units[currentUnit].getIndex()][currentWidth].add(
|
| - resKey,
|
| - rawPattern,
|
| - status);
|
| + }
|
| + return UMEASFMT_WIDTH_COUNT;
|
| + }
|
| +
|
| + static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) {
|
| + int32_t length;
|
| + const UChar *s = value.getAliasString(length, errorCode);
|
| + // For example: "/LOCALE/unitsShort"
|
| + if (U_SUCCESS(errorCode) && length >= 13 && u_memcmp(s, g_LOCALE_units, 13) == 0) {
|
| + s += 13;
|
| + length -= 13;
|
| + if (*s == 0) {
|
| + return UMEASFMT_WIDTH_WIDE;
|
| + } else if (u_strCompare(s, length, gShort, 5, FALSE) == 0) {
|
| + return UMEASFMT_WIDTH_SHORT;
|
| + } else if (u_strCompare(s, length, gNarrow, 6, FALSE) == 0) {
|
| + return UMEASFMT_WIDTH_NARROW;
|
| }
|
| }
|
| + return UMEASFMT_WIDTH_COUNT;
|
| }
|
| - delete [] units;
|
| +
|
| + // Output data.
|
| + MeasureFormatCacheData &cacheData;
|
| +
|
| + // Path to current data.
|
| + UMeasureFormatWidth width;
|
| + const char *type;
|
| + int32_t unitIndex;
|
| +};
|
| +
|
| +// Virtual destructors must be defined out of line.
|
| +UnitDataSink::UnitPatternSink::~UnitPatternSink() {}
|
| +UnitDataSink::UnitSubtypeSink::~UnitSubtypeSink() {}
|
| +UnitDataSink::UnitCompoundSink::~UnitCompoundSink() {}
|
| +UnitDataSink::UnitTypeSink::~UnitTypeSink() {}
|
| +UnitDataSink::~UnitDataSink() {}
|
| +
|
| +} // namespace
|
| +
|
| +static UBool loadMeasureUnitData(
|
| + const UResourceBundle *resource,
|
| + MeasureFormatCacheData &cacheData,
|
| + UErrorCode &status) {
|
| + UnitDataSink sink(cacheData);
|
| + ures_getAllTableItemsWithFallback(resource, "", sink, status);
|
| return U_SUCCESS(status);
|
| }
|
|
|
| @@ -486,7 +604,9 @@ MeasureFormat::MeasureFormat(const MeasureFormat &other) :
|
| cache->addRef();
|
| numberFormat->addRef();
|
| pluralRules->addRef();
|
| - listFormatter = new ListFormatter(*other.listFormatter);
|
| + if (other.listFormatter != NULL) {
|
| + listFormatter = new ListFormatter(*other.listFormatter);
|
| + }
|
| }
|
|
|
| MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
|
| @@ -499,7 +619,11 @@ MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
|
| SharedObject::copyPtr(other.pluralRules, pluralRules);
|
| width = other.width;
|
| delete listFormatter;
|
| - listFormatter = new ListFormatter(*other.listFormatter);
|
| + if (other.listFormatter != NULL) {
|
| + listFormatter = new ListFormatter(*other.listFormatter);
|
| + } else {
|
| + listFormatter = NULL;
|
| + }
|
| return *this;
|
| }
|
|
|
| @@ -714,7 +838,7 @@ void MeasureFormat::initMeasureFormat(
|
| delete listFormatter;
|
| listFormatter = ListFormatter::createInstance(
|
| locale,
|
| - listStyles[widthToIndex(width)],
|
| + listStyles[getRegularWidth(width)],
|
| status);
|
| }
|
|
|
| @@ -771,24 +895,17 @@ UnicodeString &MeasureFormat::formatMeasure(
|
| if (isCurrency(amtUnit)) {
|
| UChar isoCode[4];
|
| u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
|
| - return cache->getCurrencyFormat(widthToIndex(width))->format(
|
| + return cache->getCurrencyFormat(width)->format(
|
| new CurrencyAmount(amtNumber, isoCode, status),
|
| appendTo,
|
| pos,
|
| status);
|
| }
|
| - const QuantityFormatter *quantityFormatter = getQuantityFormatter(
|
| - amtUnit.getIndex(), widthToIndex(width), status);
|
| - if (U_FAILURE(status)) {
|
| - return appendTo;
|
| - }
|
| - return quantityFormatter->format(
|
| - amtNumber,
|
| - nf,
|
| - **pluralRules,
|
| - appendTo,
|
| - pos,
|
| - status);
|
| + UnicodeString formattedNumber;
|
| + StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
|
| + amtNumber, nf, **pluralRules, formattedNumber, pos, status);
|
| + const SimplePatternFormatter *formatter = getPluralFormatter(amtUnit, width, pluralForm, status);
|
| + return QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos, status);
|
| }
|
|
|
| // Formats hours-minutes-seconds as 5:37:23 or similar.
|
| @@ -920,64 +1037,69 @@ UnicodeString &MeasureFormat::formatNumeric(
|
| return appendTo;
|
| }
|
|
|
| -const QuantityFormatter *MeasureFormat::getQuantityFormatter(
|
| - int32_t index,
|
| - int32_t widthIndex,
|
| - UErrorCode &status) const {
|
| - if (U_FAILURE(status)) {
|
| - return NULL;
|
| +const SimplePatternFormatter *MeasureFormat::getFormatterOrNull(
|
| + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const {
|
| + width = getRegularWidth(width);
|
| + SimplePatternFormatter *const (*unitPatterns)[MeasureFormatCacheData::PATTERN_COUNT] =
|
| + &cache->patterns[unit.getIndex()][0];
|
| + if (unitPatterns[width][index] != NULL) {
|
| + return unitPatterns[width][index];
|
| }
|
| - const QuantityFormatter *formatters =
|
| - cache->formatters[index];
|
| - if (formatters[widthIndex].isValid()) {
|
| - return &formatters[widthIndex];
|
| + int32_t fallbackWidth = cache->widthFallback[width];
|
| + if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) {
|
| + return unitPatterns[fallbackWidth][index];
|
| }
|
| - if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) {
|
| - return &formatters[UMEASFMT_WIDTH_SHORT];
|
| - }
|
| - status = U_MISSING_RESOURCE_ERROR;
|
| return NULL;
|
| }
|
|
|
| -const SimplePatternFormatter *MeasureFormat::getPerUnitFormatter(
|
| - int32_t index,
|
| - int32_t widthIndex) const {
|
| - const SimplePatternFormatter * const * perUnitFormatters =
|
| - cache->getPerUnitFormattersByIndex(index);
|
| - if (perUnitFormatters[widthIndex] != NULL) {
|
| - return perUnitFormatters[widthIndex];
|
| +const SimplePatternFormatter *MeasureFormat::getFormatter(
|
| + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
|
| + UErrorCode &errorCode) const {
|
| + if (U_FAILURE(errorCode)) {
|
| + return NULL;
|
| }
|
| - if (perUnitFormatters[UMEASFMT_WIDTH_SHORT] != NULL) {
|
| - return perUnitFormatters[UMEASFMT_WIDTH_SHORT];
|
| + const SimplePatternFormatter *pattern = getFormatterOrNull(unit, width, index);
|
| + if (pattern == NULL) {
|
| + errorCode = U_MISSING_RESOURCE_ERROR;
|
| }
|
| - return NULL;
|
| + return pattern;
|
| +}
|
| +
|
| +const SimplePatternFormatter *MeasureFormat::getPluralFormatter(
|
| + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
|
| + UErrorCode &errorCode) const {
|
| + if (U_FAILURE(errorCode)) {
|
| + return NULL;
|
| + }
|
| + if (index != StandardPlural::OTHER) {
|
| + const SimplePatternFormatter *pattern = getFormatterOrNull(unit, width, index);
|
| + if (pattern != NULL) {
|
| + return pattern;
|
| + }
|
| + }
|
| + return getFormatter(unit, width, StandardPlural::OTHER, errorCode);
|
| }
|
|
|
| const SimplePatternFormatter *MeasureFormat::getPerFormatter(
|
| - int32_t widthIndex,
|
| + UMeasureFormatWidth width,
|
| UErrorCode &status) const {
|
| if (U_FAILURE(status)) {
|
| return NULL;
|
| }
|
| + width = getRegularWidth(width);
|
| const SimplePatternFormatter * perFormatters = cache->perFormatters;
|
| -
|
| - if (perFormatters[widthIndex].getPlaceholderCount() == 2) {
|
| - return &perFormatters[widthIndex];
|
| + if (perFormatters[width].getPlaceholderCount() == 2) {
|
| + return &perFormatters[width];
|
| }
|
| - if (perFormatters[UMEASFMT_WIDTH_SHORT].getPlaceholderCount() == 2) {
|
| - return &perFormatters[UMEASFMT_WIDTH_SHORT];
|
| + int32_t fallbackWidth = cache->widthFallback[width];
|
| + if (fallbackWidth != UMEASFMT_WIDTH_COUNT &&
|
| + perFormatters[fallbackWidth].getPlaceholderCount() == 2) {
|
| + return &perFormatters[fallbackWidth];
|
| }
|
| status = U_MISSING_RESOURCE_ERROR;
|
| return NULL;
|
| }
|
|
|
| -static void getPerUnitString(
|
| - const QuantityFormatter &formatter,
|
| - UnicodeString &result) {
|
| - result = formatter.getByVariant("one")->getPatternWithNoPlaceholders();
|
| - result.trim();
|
| -}
|
| -
|
| int32_t MeasureFormat::withPerUnitAndAppend(
|
| const UnicodeString &formatted,
|
| const MeasureUnit &perUnit,
|
| @@ -987,8 +1109,8 @@ int32_t MeasureFormat::withPerUnitAndAppend(
|
| if (U_FAILURE(status)) {
|
| return offset;
|
| }
|
| - const SimplePatternFormatter *perUnitFormatter = getPerUnitFormatter(
|
| - perUnit.getIndex(), widthToIndex(width));
|
| + const SimplePatternFormatter *perUnitFormatter =
|
| + getFormatterOrNull(perUnit, width, MeasureFormatCacheData::PER_UNIT_INDEX);
|
| if (perUnitFormatter != NULL) {
|
| const UnicodeString *params[] = {&formatted};
|
| perUnitFormatter->formatAndAppend(
|
| @@ -1000,15 +1122,14 @@ int32_t MeasureFormat::withPerUnitAndAppend(
|
| status);
|
| return offset;
|
| }
|
| - const SimplePatternFormatter *perFormatter = getPerFormatter(
|
| - widthToIndex(width), status);
|
| - const QuantityFormatter *qf = getQuantityFormatter(
|
| - perUnit.getIndex(), widthToIndex(width), status);
|
| + const SimplePatternFormatter *perFormatter = getPerFormatter(width, status);
|
| + const SimplePatternFormatter *pattern =
|
| + getPluralFormatter(perUnit, width, StandardPlural::ONE, status);
|
| if (U_FAILURE(status)) {
|
| return offset;
|
| }
|
| - UnicodeString perUnitString;
|
| - getPerUnitString(*qf, perUnitString);
|
| + UnicodeString perUnitString = pattern->getPatternWithNoPlaceholders();
|
| + perUnitString.trim();
|
| const UnicodeString *params[] = {&formatted, &perUnitString};
|
| perFormatter->formatAndAppend(
|
| params,
|
|
|