+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2012, International Business Machines Corporation and
+ * Copyright (c) 1997-2015, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************
*
#include "unicode/decimfmt.h"
#include "unicode/localpointer.h"
#include "unicode/msgfmt.h"
+#include "unicode/numberformatter.h"
#include "unicode/plurfmt.h"
#include "unicode/rbnf.h"
#include "unicode/selfmt.h"
#include "patternprops.h"
#include "messageimpl.h"
#include "msgfmt_impl.h"
+#include "plurrule_impl.h"
#include "uassert.h"
#include "uelement.h"
#include "uhash.h"
#include "ustrfmt.h"
#include "util.h"
#include "uvector.h"
+#include "number_decimalquantity.h"
// *****************************************************************************
// class MessageFormat
append(s);
}
}
+ void formatAndAppend(const Format* formatter, const Formattable& arg,
+ const UnicodeString &argString, UErrorCode& ec) {
+ if (!argString.isEmpty()) {
+ if (U_SUCCESS(ec)) {
+ append(argString);
+ }
+ } else {
+ formatAndAppend(formatter, arg, ec);
+ }
+ }
int32_t length() {
return len;
}
defaultDateFormat(NULL),
cachedFormatters(NULL),
customFormatArgStarts(NULL),
- pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
- ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+ pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+ ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
{
setLocaleIDs(fLocale.getName(), fLocale.getName());
applyPattern(pattern, success);
defaultDateFormat(NULL),
cachedFormatters(NULL),
customFormatArgStarts(NULL),
- pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
- ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+ pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+ ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
{
setLocaleIDs(fLocale.getName(), fLocale.getName());
applyPattern(pattern, success);
defaultDateFormat(NULL),
cachedFormatters(NULL),
customFormatArgStarts(NULL),
- pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
- ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+ pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+ ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
{
setLocaleIDs(fLocale.getName(), fLocale.getName());
applyPattern(pattern, parseError, success);
defaultDateFormat(NULL),
cachedFormatters(NULL),
customFormatArgStarts(NULL),
- pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
- ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+ pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+ ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
{
// This will take care of creating the hash tables (since they are NULL).
UErrorCode ec = U_ZERO_ERROR;
if (count != rhs_count) {
return FALSE;
}
- int32_t idx = 0, rhs_idx = 0, pos = -1, rhs_pos = -1;
+ int32_t idx = 0, rhs_idx = 0, pos = UHASH_FIRST, rhs_pos = UHASH_FIRST;
for (; idx < count && rhs_idx < rhs_count && U_SUCCESS(ec); ++idx, ++rhs_idx) {
const UHashElement* cur = uhash_nextElement(customFormatArgStarts, &pos);
const UHashElement* rhs_cur = uhash_nextElement(that.customFormatArgStarts, &rhs_pos);
// -------------------------------------
// Creates a copy of this MessageFormat, the caller owns the copy.
-Format*
+MessageFormat*
MessageFormat::clone() const
{
return new MessageFormat(*this);
defaultDateFormat = NULL;
fLocale = theLocale;
setLocaleIDs(fLocale.getName(), fLocale.getName());
- pluralProvider.reset(&fLocale);
- ordinalProvider.reset(&fLocale);
+ pluralProvider.reset();
+ ordinalProvider.reset();
}
}
UErrorCode& status) {
if (U_FAILURE(status)) {
delete formatter;
+ return;
}
if (cachedFormatters == NULL) {
cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong,
(partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status);
) {
if (argNameMatches(partIndex + 1, formatName, argNumber)) {
- if (&newFormat == NULL) {
- setCustomArgStartFormat(partIndex, NULL, status);
- } else {
- Format* new_format = newFormat.clone();
- if (new_format == NULL) {
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- setCustomArgStartFormat(partIndex, new_format, status);
+ Format* new_format = newFormat.clone();
+ if (new_format == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
}
+ setCustomArgStartFormat(partIndex, new_format, status);
}
}
}
// method on this object. We construct and resize an array
// on demand that contains aliases to the subformats[i].format
// pointers.
+
+ // Get total required capacity first (it's refreshed on each call).
+ int32_t totalCapacity = 0;
+ for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0; ++totalCapacity) {}
+
MessageFormat* t = const_cast<MessageFormat*> (this);
cnt = 0;
- if (formatAliases == NULL) {
- t->formatAliasesCapacity = (argTypeCount<10) ? 10 : argTypeCount;
+ if (formatAliases == nullptr) {
+ t->formatAliasesCapacity = totalCapacity;
Format** a = (Format**)
uprv_malloc(sizeof(Format*) * formatAliasesCapacity);
- if (a == NULL) {
+ if (a == nullptr) {
t->formatAliasesCapacity = 0;
- return NULL;
+ return nullptr;
}
t->formatAliases = a;
- } else if (argTypeCount > formatAliasesCapacity) {
+ } else if (totalCapacity > formatAliasesCapacity) {
Format** a = (Format**)
- uprv_realloc(formatAliases, sizeof(Format*) * argTypeCount);
- if (a == NULL) {
+ uprv_realloc(formatAliases, sizeof(Format*) * totalCapacity);
+ if (a == nullptr) {
t->formatAliasesCapacity = 0;
- return NULL;
+ return nullptr;
}
t->formatAliases = a;
- t->formatAliasesCapacity = argTypeCount;
+ t->formatAliasesCapacity = totalCapacity;
}
for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
UnicodeString MessageFormat::getArgName(int32_t partIndex) {
const MessagePattern::Part& part = msgPattern.getPart(partIndex);
- if (part.getType() == UMSGPAT_PART_TYPE_ARG_NAME) {
- return msgPattern.getSubstring(part);
- } else {
- UnicodeString temp;
- return itos(part.getValue(), temp);
- }
+ return msgPattern.getSubstring(part);
}
StringEnumeration*
UnicodeStringAppendable usapp(appendTo);
AppendableWrapper app(usapp);
- format(0, 0.0, arguments, argumentNames, cnt, app, pos, status);
+ format(0, NULL, arguments, argumentNames, cnt, app, pos, status);
return appendTo;
}
+namespace {
+
+/**
+ * Mutable input/output values for the PluralSelectorProvider.
+ * Separate so that it is possible to make MessageFormat Freezable.
+ */
+class PluralSelectorContext {
+public:
+ PluralSelectorContext(int32_t start, const UnicodeString &name,
+ const Formattable &num, double off, UErrorCode &errorCode)
+ : startIndex(start), argName(name), offset(off),
+ numberArgIndex(-1), formatter(NULL), forReplaceNumber(FALSE) {
+ // number needs to be set even when select() is not called.
+ // Keep it as a Number/Formattable:
+ // For format() methods, and to preserve information (e.g., BigDecimal).
+ if(off == 0) {
+ number = num;
+ } else {
+ number = num.getDouble(errorCode) - off;
+ }
+ }
+
+ // Input values for plural selection with decimals.
+ int32_t startIndex;
+ const UnicodeString &argName;
+ /** argument number - plural offset */
+ Formattable number;
+ double offset;
+ // Output values for plural selection with decimals.
+ /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */
+ int32_t numberArgIndex;
+ const Format *formatter;
+ /** formatted argument number - plural offset */
+ UnicodeString numberString;
+ /** TRUE if number-offset was formatted with the stock number formatter */
+ UBool forReplaceNumber;
+};
+
+} // namespace
+
// if argumentNames is NULL, this means arguments is a numeric array.
// arguments can not be NULL.
-void MessageFormat::format(int32_t msgStart, double pluralNumber,
+// We use const void *plNumber rather than const PluralSelectorContext *pluralNumber
+// so that we need not declare the PluralSelectorContext in the public header file.
+void MessageFormat::format(int32_t msgStart, const void *plNumber,
const Formattable* arguments,
const UnicodeString *argumentNames,
int32_t cnt,
}
prevIndex = part->getLimit();
if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
- const NumberFormat* nf = getDefaultNumberFormat(success);
- appendTo.formatAndAppend(nf, Formattable(pluralNumber), success);
+ const PluralSelectorContext &pluralNumber =
+ *static_cast<const PluralSelectorContext *>(plNumber);
+ if(pluralNumber.forReplaceNumber) {
+ // number-offset was already formatted.
+ appendTo.formatAndAppend(pluralNumber.formatter,
+ pluralNumber.number, pluralNumber.numberString, success);
+ } else {
+ const NumberFormat* nf = getDefaultNumberFormat(success);
+ appendTo.formatAndAppend(nf, pluralNumber.number, success);
+ }
continue;
}
if (type != UMSGPAT_PART_TYPE_ARG_START) {
UMessagePatternArgType argType = part->getArgType();
part = &msgPattern.getPart(++i);
const Formattable* arg;
- UnicodeString noArg;
+ UBool noArg = FALSE;
+ UnicodeString argName = msgPattern.getSubstring(*part);
if (argumentNames == NULL) {
int32_t argNumber = part->getValue(); // ARG_NUMBER
if (0 <= argNumber && argNumber < cnt) {
arg = arguments + argNumber;
} else {
arg = NULL;
- noArg.append(LEFT_CURLY_BRACE);
- itos(argNumber, noArg);
- noArg.append(RIGHT_CURLY_BRACE);
+ noArg = TRUE;
}
} else {
- UnicodeString key;
- if (part->getType() == UMSGPAT_PART_TYPE_ARG_NAME) {
- key = msgPattern.getSubstring(*part);
- } else /* UMSGPAT_PART_TYPE_ARG_NUMBER */ {
- itos(part->getValue(), key);
- }
- arg = getArgFromListByName(arguments, argumentNames, cnt, key);
+ arg = getArgFromListByName(arguments, argumentNames, cnt, argName);
if (arg == NULL) {
- noArg.append(LEFT_CURLY_BRACE);
- noArg.append(key);
- noArg.append(RIGHT_CURLY_BRACE);
+ noArg = TRUE;
}
}
++i;
int32_t prevDestLength = appendTo.length();
const Format* formatter = NULL;
- if (!noArg.isEmpty()) {
- appendTo.append(noArg);
+ if (noArg) {
+ appendTo.append(
+ UnicodeString(LEFT_CURLY_BRACE).append(argName).append(RIGHT_CURLY_BRACE));
} else if (arg == NULL) {
appendTo.append(NULL_STRING, 4);
- } else if ((formatter = getCachedFormatter(i -2))) {
+ } else if(plNumber!=NULL &&
+ static_cast<const PluralSelectorContext *>(plNumber)->numberArgIndex==(i-2)) {
+ const PluralSelectorContext &pluralNumber =
+ *static_cast<const PluralSelectorContext *>(plNumber);
+ if(pluralNumber.offset == 0) {
+ // The number was already formatted with this formatter.
+ appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number,
+ pluralNumber.numberString, success);
+ } else {
+ // Do not use the formatted (number-offset) string for a named argument
+ // that formats the number without subtracting the offset.
+ appendTo.formatAndAppend(pluralNumber.formatter, *arg, success);
+ }
+ } else if ((formatter = getCachedFormatter(i -2)) != 0) {
// Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings.
if (dynamic_cast<const ChoiceFormat*>(formatter) ||
dynamic_cast<const PluralFormat*>(formatter) ||
(subMsgString.indexOf(SINGLE_QUOTE) >= 0 && !MessageImpl::jdkAposMode(msgPattern))
) {
MessageFormat subMsgFormat(subMsgString, fLocale, success);
- subMsgFormat.format(0, 0, arguments, argumentNames, cnt, appendTo, ignore, success);
+ subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, ignore, success);
} else {
appendTo.append(subMsgString);
}
// because only this one converts non-double numeric types to double.
const double number = arg->getDouble(success);
int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number);
- formatComplexSubMessage(subMsgStart, 0, arguments, argumentNames,
+ formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames,
cnt, appendTo, success);
} else if (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)) {
if (!arg->isNumeric()) {
success = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
- const PluralFormat::PluralSelector &selector =
+ const PluralSelectorProvider &selector =
argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider;
// We must use the Formattable::getDouble() variant with the UErrorCode parameter
// because only this one converts non-double numeric types to double.
- double number = arg->getDouble(success);
- int32_t subMsgStart = PluralFormat::findSubMessage(msgPattern, i, selector, number,
- success);
double offset = msgPattern.getPluralOffset(i);
- formatComplexSubMessage(subMsgStart, number-offset, arguments, argumentNames,
+ PluralSelectorContext context(i, argName, *arg, offset, success);
+ int32_t subMsgStart = PluralFormat::findSubMessage(
+ msgPattern, i, selector, &context, arg->getDouble(success), success);
+ formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames,
cnt, appendTo, success);
} else if (argType == UMSGPAT_ARG_TYPE_SELECT) {
int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success);
- formatComplexSubMessage(subMsgStart, 0, arguments, argumentNames,
+ formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames,
cnt, appendTo, success);
} else {
// This should never happen.
void MessageFormat::formatComplexSubMessage(int32_t msgStart,
- double pluralNumber,
+ const void *plNumber,
const Formattable* arguments,
const UnicodeString *argumentNames,
int32_t cnt,
}
if (!MessageImpl::jdkAposMode(msgPattern)) {
- format(msgStart, pluralNumber, arguments, argumentNames, cnt, appendTo, NULL, success);
+ format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, NULL, success);
return;
}
} else if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
sb.append(msgString, prevIndex, index - prevIndex);
if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
- const NumberFormat* nf = getDefaultNumberFormat(success);
- sb.append(nf->format(pluralNumber, sb, success));
+ const PluralSelectorContext &pluralNumber =
+ *static_cast<const PluralSelectorContext *>(plNumber);
+ if(pluralNumber.forReplaceNumber) {
+ // number-offset was already formatted.
+ sb.append(pluralNumber.numberString);
+ } else {
+ const NumberFormat* nf = getDefaultNumberFormat(success);
+ sb.append(nf->format(pluralNumber.number, sb, success));
+ }
}
prevIndex = part.getLimit();
} else if (type == UMSGPAT_PART_TYPE_ARG_START) {
UnicodeString emptyPattern; // gcc 3.3.3 fails with "UnicodeString()" as the first parameter.
MessageFormat subMsgFormat(emptyPattern, fLocale, success);
subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, NULL, success);
- subMsgFormat.format(0, 0, arguments, argumentNames, cnt, appendTo, NULL, success);
+ subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, NULL, success);
} else {
appendTo.append(sb);
}
*/
}
+int32_t
+MessageFormat::findOtherSubMessage(int32_t partIndex) const {
+ int32_t count=msgPattern.countParts();
+ const MessagePattern::Part *part = &msgPattern.getPart(partIndex);
+ if(MessagePattern::Part::hasNumericValue(part->getType())) {
+ ++partIndex;
+ }
+ // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
+ // until ARG_LIMIT or end of plural-only pattern.
+ UnicodeString other(FALSE, OTHER_STRING, 5);
+ do {
+ part=&msgPattern.getPart(partIndex++);
+ UMessagePatternPartType type=part->getType();
+ if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) {
+ break;
+ }
+ U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR);
+ // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
+ if(msgPattern.partSubstringMatches(*part, other)) {
+ return partIndex;
+ }
+ if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) {
+ ++partIndex; // skip the numeric-value part of "=1" etc.
+ }
+ partIndex=msgPattern.getLimitPartIndex(partIndex);
+ } while(++partIndex<count);
+ return 0;
+}
+
+int32_t
+MessageFormat::findFirstPluralNumberArg(int32_t msgStart, const UnicodeString &argName) const {
+ for(int32_t i=msgStart+1;; ++i) {
+ const MessagePattern::Part &part=msgPattern.getPart(i);
+ UMessagePatternPartType type=part.getType();
+ if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
+ return 0;
+ }
+ if(type==UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
+ return -1;
+ }
+ if(type==UMSGPAT_PART_TYPE_ARG_START) {
+ UMessagePatternArgType argType=part.getArgType();
+ if(!argName.isEmpty() && (argType==UMSGPAT_ARG_TYPE_NONE || argType==UMSGPAT_ARG_TYPE_SIMPLE)) {
+ // ARG_NUMBER or ARG_NAME
+ if(msgPattern.partSubstringMatches(msgPattern.getPart(i+1), argName)) {
+ return i;
+ }
+ }
+ i=msgPattern.getLimitPartIndex(i);
+ }
+ }
+}
+
void MessageFormat::copyObjects(const MessageFormat& that, UErrorCode& ec) {
// Deep copy pointer fields.
// We need not copy the formatAliases because they are re-filled
const int32_t count = uhash_count(that.cachedFormatters);
int32_t pos, idx;
- for (idx = 0, pos = -1; idx < count && U_SUCCESS(ec); ++idx) {
+ for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) {
const UHashElement* cur = uhash_nextElement(that.cachedFormatters, &pos);
Format* newFormat = ((Format*)(cur->value.pointer))->clone();
if (newFormat) {
}
const int32_t count = uhash_count(that.customFormatArgStarts);
int32_t pos, idx;
- for (idx = 0, pos = -1; idx < count && U_SUCCESS(ec); ++idx) {
+ for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) {
const UHashElement* cur = uhash_nextElement(that.customFormatArgStarts, &pos);
uhash_iputi(customFormatArgStarts, cur->key.integer, cur->value.integer, &ec);
}
}
}
-
Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeString& style,
Formattable::Type& formattableType, UParseError& parseError,
UErrorCode& ec) {
Format* fmt = NULL;
int32_t typeID, styleID;
DateFormat::EStyle date_style;
+ int32_t firstNonSpace;
switch (typeID = findKeyword(type, TYPE_IDS)) {
case 0: // number
formattableType = Formattable::kLong;
fmt = createIntegerFormat(fLocale, ec);
break;
- default: // pattern
- fmt = NumberFormat::createInstance(fLocale, ec);
- if (fmt) {
- DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fmt);
- if (decfmt != NULL) {
- decfmt->applyPattern(style,parseError,ec);
+ default: // pattern or skeleton
+ firstNonSpace = PatternProps::skipWhiteSpace(style, 0);
+ if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) {
+ // Skeleton
+ UnicodeString skeleton = style.tempSubString(firstNonSpace + 2);
+ fmt = number::NumberFormatter::forSkeleton(skeleton, ec).locale(fLocale).toFormat(ec);
+ } else {
+ // Pattern
+ fmt = NumberFormat::createInstance(fLocale, ec);
+ if (fmt) {
+ auto* decfmt = dynamic_cast<DecimalFormat*>(fmt);
+ if (decfmt != nullptr) {
+ decfmt->applyPattern(style, parseError, ec);
+ }
}
}
break;
case 1: // date
case 2: // time
formattableType = Formattable::kDate;
- styleID = findKeyword(style, DATE_STYLE_IDS);
- date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault;
-
- if (typeID == 1) {
- fmt = DateFormat::createDateInstance(date_style, fLocale);
+ firstNonSpace = PatternProps::skipWhiteSpace(style, 0);
+ if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) {
+ // Skeleton
+ UnicodeString skeleton = style.tempSubString(firstNonSpace + 2);
+ fmt = DateFormat::createInstanceForSkeleton(skeleton, fLocale, ec);
} else {
- fmt = DateFormat::createTimeInstance(date_style, fLocale);
- }
+ // Pattern
+ styleID = findKeyword(style, DATE_STYLE_IDS);
+ date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault;
+
+ if (typeID == 1) {
+ fmt = DateFormat::createDateInstance(date_style, fLocale);
+ } else {
+ fmt = DateFormat::createTimeInstance(date_style, fLocale);
+ }
- if (styleID < 0 && fmt != NULL) {
- SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fmt);
- if (sdtfmt != NULL) {
- sdtfmt->applyPattern(style);
+ if (styleID < 0 && fmt != NULL) {
+ SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fmt);
+ if (sdtfmt != NULL) {
+ sdtfmt->applyPattern(style);
+ }
}
}
break;
return TRUE;
}
-Format* MessageFormat::DummyFormat::clone() const {
+MessageFormat::DummyFormat* MessageFormat::DummyFormat::clone() const {
return new DummyFormat();
}
delete fFormatNames;
}
-
-MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const Locale* loc, UPluralType t)
- : locale(loc), rules(NULL), type(t) {
+MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t)
+ : msgFormat(mf), rules(NULL), type(t) {
}
MessageFormat::PluralSelectorProvider::~PluralSelectorProvider() {
- // We own the rules but not the locale.
delete rules;
}
-UnicodeString MessageFormat::PluralSelectorProvider::select(double number, UErrorCode& ec) const {
+UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double number,
+ UErrorCode& ec) const {
if (U_FAILURE(ec)) {
return UnicodeString(FALSE, OTHER_STRING, 5);
}
MessageFormat::PluralSelectorProvider* t = const_cast<MessageFormat::PluralSelectorProvider*>(this);
if(rules == NULL) {
- t->rules = PluralRules::forLocale(*locale, type, ec);
+ t->rules = PluralRules::forLocale(msgFormat.fLocale, type, ec);
if (U_FAILURE(ec)) {
return UnicodeString(FALSE, OTHER_STRING, 5);
}
}
- return rules->select(number);
+ // Select a sub-message according to how the number is formatted,
+ // which is specified in the selected sub-message.
+ // We avoid this circle by looking at how
+ // the number is formatted in the "other" sub-message
+ // which must always be present and usually contains the number.
+ // Message authors should be consistent across sub-messages.
+ PluralSelectorContext &context = *static_cast<PluralSelectorContext *>(ctx);
+ int32_t otherIndex = msgFormat.findOtherSubMessage(context.startIndex);
+ context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName);
+ if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != NULL) {
+ context.formatter =
+ (const Format*)uhash_iget(msgFormat.cachedFormatters, context.numberArgIndex);
+ }
+ if(context.formatter == NULL) {
+ context.formatter = msgFormat.getDefaultNumberFormat(ec);
+ context.forReplaceNumber = TRUE;
+ }
+ if (context.number.getDouble(ec) != number) {
+ ec = U_INTERNAL_PROGRAM_ERROR;
+ return UnicodeString(FALSE, OTHER_STRING, 5);
+ }
+ context.formatter->format(context.number, context.numberString, ec);
+ auto* decFmt = dynamic_cast<const DecimalFormat *>(context.formatter);
+ if(decFmt != NULL) {
+ number::impl::DecimalQuantity dq;
+ decFmt->formatToDecimalQuantity(context.number, dq, ec);
+ if (U_FAILURE(ec)) {
+ return UnicodeString(FALSE, OTHER_STRING, 5);
+ }
+ return rules->select(dq);
+ } else {
+ return rules->select(number);
+ }
}
-void MessageFormat::PluralSelectorProvider::reset(const Locale* loc) {
- locale = loc;
+void MessageFormat::PluralSelectorProvider::reset() {
delete rules;
rules = NULL;
}