gecko/intl/icu/source/i18n/plurfmt.cpp
Jeff Walden 805dd78c93 Bug 924839 - Update our embedded ICU to 52.1, plus a very few local patches. r=lots of people, see subsequent lines in this commit message for the original subcomponents (merged together for landing), and the original bug for the original patch divisions
Bug 924839 - Remove a patch already part of ICU 52.1.  See http://bugs.icu-project.org/trac/ticket/10283 but also note the relevant code was removed completely upstream.  r=glandium
* * *
Bug 924839 - Remove another patch already part of ICU 52.1.  See http://bugs.icu-project.org/trac/ticket/10290 for that.  r=gaston
* * *
Bug 924839 - Remove another patch already in ICU 52.1.  See http://bugs.icu-project.org/trac/ticket/10045 for more.  r=Norbert
* * *
Bug 924839 - Remove another patch already applied upstream.  See http://bugs.icu-project.org/trac/changeset/32937 for more.  r=gaston
* * *
Bug 924839 - Update the ICU update script to update to 52.1, *without* applying any of our local patches.  r=glandium
* * *
Bug 924839 - Make the ICU update script only do updating within intl/icu/source and nowhere else.  r=glandium
* * *
Bug 924839 - Implement the changes that would be made by |cd intl/; ./update-icu.sh http://source.icu-project.org/repos/icu/icu/tags/release-52-1/;|, run with the prior changesets' changes made (thus not applying any of our local patches).  These changes don't actually work without subsequent adjustments, but this provides a codebase upon which those adjustments can be made, for the purpose of generating local patches to be kept in intl/icu-patches/.  rs=the-usual-suspects
* * *
Bug 924839 - Update the bug 899722 local patch to make runConfigureICU not override CC/CXX on BSD systems.  r=gaston
* * *
Bug 924839 - Update the bug 724533 patch that makes ICU builds with MozillaBuild on Windows.  r=glandium
* * *
Bug 924839 - Import an upstream patch fixing the genrb tool to properly handle the -R (--omitCollationRules) option.  See http://bugs.icu-project.org/trac/ticket/10043 for the original bug report and a link to the ultimate upstream landing.  r=Norbert
* * *
Bug 924839 - Import the upstream fix for http://bugs.icu-project.org/trac/ticket/10486 so that ICU with -DU_USING_ICU_NAMESPACE=0 will compile on Windows.  r=Norbert
* * *
Bug 924839 - Adjust the update script to update ICU, then to apply all local patches (rather than skipping the second step).  Thus if the update script is properly run, now, the final result should be no changes at all to the tree.  NOT REVIEWED YET
* * *
Bug 924839 - Update jstests that depend on CLDR locale data to match CLDR 24.  r=Norbert
2013-11-12 16:23:48 -08:00

510 lines
16 KiB
C++

/*
*******************************************************************************
* Copyright (C) 2009-2013, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* File PLURFMT.CPP
*******************************************************************************
*/
#include "unicode/decimfmt.h"
#include "unicode/messagepattern.h"
#include "unicode/plurfmt.h"
#include "unicode/plurrule.h"
#include "unicode/utypes.h"
#include "cmemory.h"
#include "messageimpl.h"
#include "plurrule_impl.h"
#include "uassert.h"
#include "uhash.h"
#if !UCONFIG_NO_FORMATTING
U_NAMESPACE_BEGIN
static const UChar OTHER_STRING[] = {
0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other"
};
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)
PluralFormat::PluralFormat(UErrorCode& status)
: locale(Locale::getDefault()),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(NULL, UPLURAL_TYPE_CARDINAL, status);
}
PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status)
: locale(loc),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(NULL, UPLURAL_TYPE_CARDINAL, status);
}
PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status)
: locale(Locale::getDefault()),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(&rules, UPLURAL_TYPE_COUNT, status);
}
PluralFormat::PluralFormat(const Locale& loc,
const PluralRules& rules,
UErrorCode& status)
: locale(loc),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(&rules, UPLURAL_TYPE_COUNT, status);
}
PluralFormat::PluralFormat(const Locale& loc,
UPluralType type,
UErrorCode& status)
: locale(loc),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(NULL, type, status);
}
PluralFormat::PluralFormat(const UnicodeString& pat,
UErrorCode& status)
: locale(Locale::getDefault()),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(NULL, UPLURAL_TYPE_CARDINAL, status);
applyPattern(pat, status);
}
PluralFormat::PluralFormat(const Locale& loc,
const UnicodeString& pat,
UErrorCode& status)
: locale(loc),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(NULL, UPLURAL_TYPE_CARDINAL, status);
applyPattern(pat, status);
}
PluralFormat::PluralFormat(const PluralRules& rules,
const UnicodeString& pat,
UErrorCode& status)
: locale(Locale::getDefault()),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(&rules, UPLURAL_TYPE_COUNT, status);
applyPattern(pat, status);
}
PluralFormat::PluralFormat(const Locale& loc,
const PluralRules& rules,
const UnicodeString& pat,
UErrorCode& status)
: locale(loc),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(&rules, UPLURAL_TYPE_COUNT, status);
applyPattern(pat, status);
}
PluralFormat::PluralFormat(const Locale& loc,
UPluralType type,
const UnicodeString& pat,
UErrorCode& status)
: locale(loc),
msgPattern(status),
numberFormat(NULL),
offset(0) {
init(NULL, type, status);
applyPattern(pat, status);
}
PluralFormat::PluralFormat(const PluralFormat& other)
: Format(other),
locale(other.locale),
msgPattern(other.msgPattern),
numberFormat(NULL),
offset(other.offset) {
copyObjects(other);
}
void
PluralFormat::copyObjects(const PluralFormat& other) {
UErrorCode status = U_ZERO_ERROR;
if (numberFormat != NULL) {
delete numberFormat;
}
if (pluralRulesWrapper.pluralRules != NULL) {
delete pluralRulesWrapper.pluralRules;
}
if (other.numberFormat == NULL) {
numberFormat = NumberFormat::createInstance(locale, status);
} else {
numberFormat = (NumberFormat*)other.numberFormat->clone();
}
if (other.pluralRulesWrapper.pluralRules == NULL) {
pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status);
} else {
pluralRulesWrapper.pluralRules = other.pluralRulesWrapper.pluralRules->clone();
}
}
PluralFormat::~PluralFormat() {
delete numberFormat;
}
void
PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (rules==NULL) {
pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status);
} else {
pluralRulesWrapper.pluralRules = rules->clone();
if (pluralRulesWrapper.pluralRules == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
}
numberFormat= NumberFormat::createInstance(locale, status);
}
void
PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
msgPattern.parsePluralStyle(newPattern, NULL, status);
if (U_FAILURE(status)) {
msgPattern.clear();
offset = 0;
return;
}
offset = msgPattern.getPluralOffset(0);
}
UnicodeString&
PluralFormat::format(const Formattable& obj,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const
{
if (U_FAILURE(status)) return appendTo;
if (obj.isNumeric()) {
return format(obj, obj.getDouble(), appendTo, pos, status);
} else {
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
}
UnicodeString
PluralFormat::format(int32_t number, UErrorCode& status) const {
FieldPosition fpos(0);
UnicodeString result;
return format(Formattable(number), number, result, fpos, status);
}
UnicodeString
PluralFormat::format(double number, UErrorCode& status) const {
FieldPosition fpos(0);
UnicodeString result;
return format(Formattable(number), number, result, fpos, status);
}
UnicodeString&
PluralFormat::format(int32_t number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
return format(Formattable(number), (double)number, appendTo, pos, status);
}
UnicodeString&
PluralFormat::format(double number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
return format(Formattable(number), (double)number, appendTo, pos, status);
}
UnicodeString&
PluralFormat::format(const Formattable& numberObject, double number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
if (U_FAILURE(status)) {
return appendTo;
}
if (msgPattern.countParts() == 0) {
return numberFormat->format(numberObject, appendTo, pos, status);
}
// Get the appropriate sub-message.
// Select it based on the formatted number-offset.
double numberMinusOffset = number - offset;
UnicodeString numberString;
FieldPosition ignorePos;
FixedDecimal dec(numberMinusOffset);
if (offset == 0) {
numberFormat->format(numberObject, numberString, ignorePos, status); // could be BigDecimal etc.
DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
if(decFmt != NULL) {
dec = decFmt->getFixedDecimal(numberObject, status);
}
} else {
numberFormat->format(numberMinusOffset, numberString, ignorePos, status);
DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
if(decFmt != NULL) {
dec = decFmt->getFixedDecimal(numberMinusOffset, status);
}
}
int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &dec, number, status);
if (U_FAILURE(status)) { return appendTo; }
// Replace syntactic # signs in the top level of this sub-message
// (not in nested arguments) with the formatted number-offset.
const UnicodeString& pattern = msgPattern.getPatternString();
int32_t prevIndex = msgPattern.getPart(partIndex).getLimit();
for (;;) {
const MessagePattern::Part& part = msgPattern.getPart(++partIndex);
const UMessagePatternPartType type = part.getType();
int32_t index = part.getIndex();
if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) {
return appendTo.append(pattern, prevIndex, index - prevIndex);
} else if ((type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) ||
(type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) {
appendTo.append(pattern, prevIndex, index - prevIndex);
if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
appendTo.append(numberString);
}
prevIndex = part.getLimit();
} else if (type == UMSGPAT_PART_TYPE_ARG_START) {
appendTo.append(pattern, prevIndex, index - prevIndex);
prevIndex = index;
partIndex = msgPattern.getLimitPartIndex(partIndex);
index = msgPattern.getPart(partIndex).getLimit();
MessageImpl::appendReducedApostrophes(pattern, prevIndex, index, appendTo);
prevIndex = index;
}
}
}
UnicodeString&
PluralFormat::toPattern(UnicodeString& appendTo) {
if (0 == msgPattern.countParts()) {
appendTo.setToBogus();
} else {
appendTo.append(msgPattern.getPatternString());
}
return appendTo;
}
void
PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
locale = loc;
msgPattern.clear();
delete numberFormat;
offset = 0;
numberFormat = NULL;
pluralRulesWrapper.reset();
init(NULL, UPLURAL_TYPE_CARDINAL, status);
}
void
PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
NumberFormat* nf = (NumberFormat*)format->clone();
if (nf != NULL) {
delete numberFormat;
numberFormat = nf;
} else {
status = U_MEMORY_ALLOCATION_ERROR;
}
}
Format*
PluralFormat::clone() const
{
return new PluralFormat(*this);
}
PluralFormat&
PluralFormat::operator=(const PluralFormat& other) {
if (this != &other) {
locale = other.locale;
msgPattern = other.msgPattern;
offset = other.offset;
copyObjects(other);
}
return *this;
}
UBool
PluralFormat::operator==(const Format& other) const {
if (this == &other) {
return TRUE;
}
if (!Format::operator==(other)) {
return FALSE;
}
const PluralFormat& o = (const PluralFormat&)other;
return
locale == o.locale &&
msgPattern == o.msgPattern && // implies same offset
(numberFormat == NULL) == (o.numberFormat == NULL) &&
(numberFormat == NULL || *numberFormat == *o.numberFormat) &&
(pluralRulesWrapper.pluralRules == NULL) == (o.pluralRulesWrapper.pluralRules == NULL) &&
(pluralRulesWrapper.pluralRules == NULL ||
*pluralRulesWrapper.pluralRules == *o.pluralRulesWrapper.pluralRules);
}
UBool
PluralFormat::operator!=(const Format& other) const {
return !operator==(other);
}
void
PluralFormat::parseObject(const UnicodeString& /*source*/,
Formattable& /*result*/,
ParsePosition& pos) const
{
// Parsing not supported.
pos.setErrorIndex(pos.getIndex());
}
int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,
const PluralSelector& selector, void *context,
double number, UErrorCode& ec) {
if (U_FAILURE(ec)) {
return 0;
}
int32_t count=pattern.countParts();
double offset;
const MessagePattern::Part* part=&pattern.getPart(partIndex);
if (MessagePattern::Part::hasNumericValue(part->getType())) {
offset=pattern.getNumericValue(*part);
++partIndex;
} else {
offset=0;
}
// The keyword is empty until we need to match against a non-explicit, not-"other" value.
// Then we get the keyword from the selector.
// (In other words, we never call the selector if we match against an explicit value,
// or if the only non-explicit keyword is "other".)
UnicodeString keyword;
UnicodeString other(FALSE, OTHER_STRING, 5);
// When we find a match, we set msgStart>0 and also set this boolean to true
// to avoid matching the keyword again (duplicates are allowed)
// while we continue to look for an explicit-value match.
UBool haveKeywordMatch=FALSE;
// msgStart is 0 until we find any appropriate sub-message.
// We remember the first "other" sub-message if we have not seen any
// appropriate sub-message before.
// We remember the first matching-keyword sub-message if we have not seen
// one of those before.
// (The parser allows [does not check for] duplicate keywords.
// We just have to make sure to take the first one.)
// We avoid matching the keyword twice by also setting haveKeywordMatch=true
// at the first keyword match.
// We keep going until we find an explicit-value match or reach the end of the plural style.
int32_t msgStart=0;
// Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
// until ARG_LIMIT or end of plural-only pattern.
do {
part=&pattern.getPart(partIndex++);
const 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(MessagePattern::Part::hasNumericValue(pattern.getPartType(partIndex))) {
// explicit value like "=2"
part=&pattern.getPart(partIndex++);
if(number==pattern.getNumericValue(*part)) {
// matches explicit value
return partIndex;
}
} else if(!haveKeywordMatch) {
// plural keyword like "few" or "other"
// Compare "other" first and call the selector if this is not "other".
if(pattern.partSubstringMatches(*part, other)) {
if(msgStart==0) {
msgStart=partIndex;
if(0 == keyword.compare(other)) {
// This is the first "other" sub-message,
// and the selected keyword is also "other".
// Do not match "other" again.
haveKeywordMatch=TRUE;
}
}
} else {
if(keyword.isEmpty()) {
keyword=selector.select(context, number-offset, ec);
if(msgStart!=0 && (0 == keyword.compare(other))) {
// We have already seen an "other" sub-message.
// Do not match "other" again.
haveKeywordMatch=TRUE;
// Skip keyword matching but do getLimitPartIndex().
}
}
if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) {
// keyword matches
msgStart=partIndex;
// Do not match this keyword again.
haveKeywordMatch=TRUE;
}
}
}
partIndex=pattern.getLimitPartIndex(partIndex);
} while(++partIndex<count);
return msgStart;
}
PluralFormat::PluralSelector::~PluralSelector() {}
PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() {
delete pluralRules;
}
UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number,
UErrorCode& /*ec*/) const {
(void)number; // unused except in the assertion
FixedDecimal *dec=static_cast<FixedDecimal *>(context);
U_ASSERT(dec->source==number);
return pluralRules->select(*dec);
}
void PluralFormat::PluralSelectorAdapter::reset() {
delete pluralRules;
pluralRules = NULL;
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof