blob: 22f6939d43e08fdaecfb1bc7fea6d51f9caaa8ad [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2009-2012, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* File PLURFMT.CPP
*
* Modification History:
*
* Date Name Description
*******************************************************************************
*/
#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.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(number, result, fpos, status);
}
UnicodeString
PluralFormat::format(double number, UErrorCode& status) const {
FieldPosition fpos(0);
UnicodeString result;
return format(number, result, fpos, status);
}
UnicodeString&
PluralFormat::format(int32_t number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
return format((double)number, appendTo, pos, status);
}
UnicodeString&
PluralFormat::format(double number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
if (U_FAILURE(status)) {
return appendTo;
}
if (msgPattern.countParts() == 0) {
return numberFormat->format(number, appendTo, pos);
}
// Get the appropriate sub-message.
int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, number, status);
// 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();
number -= offset;
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) {
numberFormat->format(number, appendTo);
}
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, 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 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(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(double number,
UErrorCode& /*ec*/) const {
return pluralRules->select(number);
}
void PluralFormat::PluralSelectorAdapter::reset() {
delete pluralRules;
pluralRules = NULL;
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof