1/* 2******************************************************************************* 3* Copyright (C) 2009-2013, International Business Machines Corporation and 4* others. All Rights Reserved. 5******************************************************************************* 6* 7* File PLURFMT.CPP 8******************************************************************************* 9*/ 10 11#include "unicode/decimfmt.h" 12#include "unicode/messagepattern.h" 13#include "unicode/plurfmt.h" 14#include "unicode/plurrule.h" 15#include "unicode/utypes.h" 16#include "cmemory.h" 17#include "messageimpl.h" 18#include "plurrule_impl.h" 19#include "uassert.h" 20#include "uhash.h" 21 22#if !UCONFIG_NO_FORMATTING 23 24U_NAMESPACE_BEGIN 25 26static const UChar OTHER_STRING[] = { 27 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" 28}; 29 30UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat) 31 32PluralFormat::PluralFormat(UErrorCode& status) 33 : locale(Locale::getDefault()), 34 msgPattern(status), 35 numberFormat(NULL), 36 offset(0) { 37 init(NULL, UPLURAL_TYPE_CARDINAL, status); 38} 39 40PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) 41 : locale(loc), 42 msgPattern(status), 43 numberFormat(NULL), 44 offset(0) { 45 init(NULL, UPLURAL_TYPE_CARDINAL, status); 46} 47 48PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) 49 : locale(Locale::getDefault()), 50 msgPattern(status), 51 numberFormat(NULL), 52 offset(0) { 53 init(&rules, UPLURAL_TYPE_COUNT, status); 54} 55 56PluralFormat::PluralFormat(const Locale& loc, 57 const PluralRules& rules, 58 UErrorCode& status) 59 : locale(loc), 60 msgPattern(status), 61 numberFormat(NULL), 62 offset(0) { 63 init(&rules, UPLURAL_TYPE_COUNT, status); 64} 65 66PluralFormat::PluralFormat(const Locale& loc, 67 UPluralType type, 68 UErrorCode& status) 69 : locale(loc), 70 msgPattern(status), 71 numberFormat(NULL), 72 offset(0) { 73 init(NULL, type, status); 74} 75 76PluralFormat::PluralFormat(const UnicodeString& pat, 77 UErrorCode& status) 78 : locale(Locale::getDefault()), 79 msgPattern(status), 80 numberFormat(NULL), 81 offset(0) { 82 init(NULL, UPLURAL_TYPE_CARDINAL, status); 83 applyPattern(pat, status); 84} 85 86PluralFormat::PluralFormat(const Locale& loc, 87 const UnicodeString& pat, 88 UErrorCode& status) 89 : locale(loc), 90 msgPattern(status), 91 numberFormat(NULL), 92 offset(0) { 93 init(NULL, UPLURAL_TYPE_CARDINAL, status); 94 applyPattern(pat, status); 95} 96 97PluralFormat::PluralFormat(const PluralRules& rules, 98 const UnicodeString& pat, 99 UErrorCode& status) 100 : locale(Locale::getDefault()), 101 msgPattern(status), 102 numberFormat(NULL), 103 offset(0) { 104 init(&rules, UPLURAL_TYPE_COUNT, status); 105 applyPattern(pat, status); 106} 107 108PluralFormat::PluralFormat(const Locale& loc, 109 const PluralRules& rules, 110 const UnicodeString& pat, 111 UErrorCode& status) 112 : locale(loc), 113 msgPattern(status), 114 numberFormat(NULL), 115 offset(0) { 116 init(&rules, UPLURAL_TYPE_COUNT, status); 117 applyPattern(pat, status); 118} 119 120PluralFormat::PluralFormat(const Locale& loc, 121 UPluralType type, 122 const UnicodeString& pat, 123 UErrorCode& status) 124 : locale(loc), 125 msgPattern(status), 126 numberFormat(NULL), 127 offset(0) { 128 init(NULL, type, status); 129 applyPattern(pat, status); 130} 131 132PluralFormat::PluralFormat(const PluralFormat& other) 133 : Format(other), 134 locale(other.locale), 135 msgPattern(other.msgPattern), 136 numberFormat(NULL), 137 offset(other.offset) { 138 copyObjects(other); 139} 140 141void 142PluralFormat::copyObjects(const PluralFormat& other) { 143 UErrorCode status = U_ZERO_ERROR; 144 if (numberFormat != NULL) { 145 delete numberFormat; 146 } 147 if (pluralRulesWrapper.pluralRules != NULL) { 148 delete pluralRulesWrapper.pluralRules; 149 } 150 151 if (other.numberFormat == NULL) { 152 numberFormat = NumberFormat::createInstance(locale, status); 153 } else { 154 numberFormat = (NumberFormat*)other.numberFormat->clone(); 155 } 156 if (other.pluralRulesWrapper.pluralRules == NULL) { 157 pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status); 158 } else { 159 pluralRulesWrapper.pluralRules = other.pluralRulesWrapper.pluralRules->clone(); 160 } 161} 162 163 164PluralFormat::~PluralFormat() { 165 delete numberFormat; 166} 167 168void 169PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) { 170 if (U_FAILURE(status)) { 171 return; 172 } 173 174 if (rules==NULL) { 175 pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status); 176 } else { 177 pluralRulesWrapper.pluralRules = rules->clone(); 178 if (pluralRulesWrapper.pluralRules == NULL) { 179 status = U_MEMORY_ALLOCATION_ERROR; 180 return; 181 } 182 } 183 184 numberFormat= NumberFormat::createInstance(locale, status); 185} 186 187void 188PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { 189 msgPattern.parsePluralStyle(newPattern, NULL, status); 190 if (U_FAILURE(status)) { 191 msgPattern.clear(); 192 offset = 0; 193 return; 194 } 195 offset = msgPattern.getPluralOffset(0); 196} 197 198UnicodeString& 199PluralFormat::format(const Formattable& obj, 200 UnicodeString& appendTo, 201 FieldPosition& pos, 202 UErrorCode& status) const 203{ 204 if (U_FAILURE(status)) return appendTo; 205 206 if (obj.isNumeric()) { 207 return format(obj, obj.getDouble(), appendTo, pos, status); 208 } else { 209 status = U_ILLEGAL_ARGUMENT_ERROR; 210 return appendTo; 211 } 212} 213 214UnicodeString 215PluralFormat::format(int32_t number, UErrorCode& status) const { 216 FieldPosition fpos(0); 217 UnicodeString result; 218 return format(Formattable(number), number, result, fpos, status); 219} 220 221UnicodeString 222PluralFormat::format(double number, UErrorCode& status) const { 223 FieldPosition fpos(0); 224 UnicodeString result; 225 return format(Formattable(number), number, result, fpos, status); 226} 227 228 229UnicodeString& 230PluralFormat::format(int32_t number, 231 UnicodeString& appendTo, 232 FieldPosition& pos, 233 UErrorCode& status) const { 234 return format(Formattable(number), (double)number, appendTo, pos, status); 235} 236 237UnicodeString& 238PluralFormat::format(double number, 239 UnicodeString& appendTo, 240 FieldPosition& pos, 241 UErrorCode& status) const { 242 return format(Formattable(number), (double)number, appendTo, pos, status); 243} 244 245UnicodeString& 246PluralFormat::format(const Formattable& numberObject, double number, 247 UnicodeString& appendTo, 248 FieldPosition& pos, 249 UErrorCode& status) const { 250 if (U_FAILURE(status)) { 251 return appendTo; 252 } 253 if (msgPattern.countParts() == 0) { 254 return numberFormat->format(numberObject, appendTo, pos, status); 255 } 256 // Get the appropriate sub-message. 257 // Select it based on the formatted number-offset. 258 double numberMinusOffset = number - offset; 259 UnicodeString numberString; 260 FieldPosition ignorePos; 261 FixedDecimal dec(numberMinusOffset); 262 if (offset == 0) { 263 numberFormat->format(numberObject, numberString, ignorePos, status); // could be BigDecimal etc. 264 DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat); 265 if(decFmt != NULL) { 266 dec = decFmt->getFixedDecimal(numberObject, status); 267 } 268 } else { 269 numberFormat->format(numberMinusOffset, numberString, ignorePos, status); 270 DecimalFormat *decFmt = dynamic_cast<DecimalFormat *>(numberFormat); 271 if(decFmt != NULL) { 272 dec = decFmt->getFixedDecimal(numberMinusOffset, status); 273 } 274 } 275 int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &dec, number, status); 276 if (U_FAILURE(status)) { return appendTo; } 277 // Replace syntactic # signs in the top level of this sub-message 278 // (not in nested arguments) with the formatted number-offset. 279 const UnicodeString& pattern = msgPattern.getPatternString(); 280 int32_t prevIndex = msgPattern.getPart(partIndex).getLimit(); 281 for (;;) { 282 const MessagePattern::Part& part = msgPattern.getPart(++partIndex); 283 const UMessagePatternPartType type = part.getType(); 284 int32_t index = part.getIndex(); 285 if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { 286 return appendTo.append(pattern, prevIndex, index - prevIndex); 287 } else if ((type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) || 288 (type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) { 289 appendTo.append(pattern, prevIndex, index - prevIndex); 290 if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { 291 appendTo.append(numberString); 292 } 293 prevIndex = part.getLimit(); 294 } else if (type == UMSGPAT_PART_TYPE_ARG_START) { 295 appendTo.append(pattern, prevIndex, index - prevIndex); 296 prevIndex = index; 297 partIndex = msgPattern.getLimitPartIndex(partIndex); 298 index = msgPattern.getPart(partIndex).getLimit(); 299 MessageImpl::appendReducedApostrophes(pattern, prevIndex, index, appendTo); 300 prevIndex = index; 301 } 302 } 303} 304 305UnicodeString& 306PluralFormat::toPattern(UnicodeString& appendTo) { 307 if (0 == msgPattern.countParts()) { 308 appendTo.setToBogus(); 309 } else { 310 appendTo.append(msgPattern.getPatternString()); 311 } 312 return appendTo; 313} 314 315void 316PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { 317 if (U_FAILURE(status)) { 318 return; 319 } 320 locale = loc; 321 msgPattern.clear(); 322 delete numberFormat; 323 offset = 0; 324 numberFormat = NULL; 325 pluralRulesWrapper.reset(); 326 init(NULL, UPLURAL_TYPE_CARDINAL, status); 327} 328 329void 330PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) { 331 if (U_FAILURE(status)) { 332 return; 333 } 334 NumberFormat* nf = (NumberFormat*)format->clone(); 335 if (nf != NULL) { 336 delete numberFormat; 337 numberFormat = nf; 338 } else { 339 status = U_MEMORY_ALLOCATION_ERROR; 340 } 341} 342 343Format* 344PluralFormat::clone() const 345{ 346 return new PluralFormat(*this); 347} 348 349 350PluralFormat& 351PluralFormat::operator=(const PluralFormat& other) { 352 if (this != &other) { 353 locale = other.locale; 354 msgPattern = other.msgPattern; 355 offset = other.offset; 356 copyObjects(other); 357 } 358 359 return *this; 360} 361 362UBool 363PluralFormat::operator==(const Format& other) const { 364 if (this == &other) { 365 return TRUE; 366 } 367 if (!Format::operator==(other)) { 368 return FALSE; 369 } 370 const PluralFormat& o = (const PluralFormat&)other; 371 return 372 locale == o.locale && 373 msgPattern == o.msgPattern && // implies same offset 374 (numberFormat == NULL) == (o.numberFormat == NULL) && 375 (numberFormat == NULL || *numberFormat == *o.numberFormat) && 376 (pluralRulesWrapper.pluralRules == NULL) == (o.pluralRulesWrapper.pluralRules == NULL) && 377 (pluralRulesWrapper.pluralRules == NULL || 378 *pluralRulesWrapper.pluralRules == *o.pluralRulesWrapper.pluralRules); 379} 380 381UBool 382PluralFormat::operator!=(const Format& other) const { 383 return !operator==(other); 384} 385 386void 387PluralFormat::parseObject(const UnicodeString& /*source*/, 388 Formattable& /*result*/, 389 ParsePosition& pos) const 390{ 391 // Parsing not supported. 392 pos.setErrorIndex(pos.getIndex()); 393} 394 395int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex, 396 const PluralSelector& selector, void *context, 397 double number, UErrorCode& ec) { 398 if (U_FAILURE(ec)) { 399 return 0; 400 } 401 int32_t count=pattern.countParts(); 402 double offset; 403 const MessagePattern::Part* part=&pattern.getPart(partIndex); 404 if (MessagePattern::Part::hasNumericValue(part->getType())) { 405 offset=pattern.getNumericValue(*part); 406 ++partIndex; 407 } else { 408 offset=0; 409 } 410 // The keyword is empty until we need to match against a non-explicit, not-"other" value. 411 // Then we get the keyword from the selector. 412 // (In other words, we never call the selector if we match against an explicit value, 413 // or if the only non-explicit keyword is "other".) 414 UnicodeString keyword; 415 UnicodeString other(FALSE, OTHER_STRING, 5); 416 // When we find a match, we set msgStart>0 and also set this boolean to true 417 // to avoid matching the keyword again (duplicates are allowed) 418 // while we continue to look for an explicit-value match. 419 UBool haveKeywordMatch=FALSE; 420 // msgStart is 0 until we find any appropriate sub-message. 421 // We remember the first "other" sub-message if we have not seen any 422 // appropriate sub-message before. 423 // We remember the first matching-keyword sub-message if we have not seen 424 // one of those before. 425 // (The parser allows [does not check for] duplicate keywords. 426 // We just have to make sure to take the first one.) 427 // We avoid matching the keyword twice by also setting haveKeywordMatch=true 428 // at the first keyword match. 429 // We keep going until we find an explicit-value match or reach the end of the plural style. 430 int32_t msgStart=0; 431 // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples 432 // until ARG_LIMIT or end of plural-only pattern. 433 do { 434 part=&pattern.getPart(partIndex++); 435 const UMessagePatternPartType type = part->getType(); 436 if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { 437 break; 438 } 439 U_ASSERT (type==UMSGPAT_PART_TYPE_ARG_SELECTOR); 440 // part is an ARG_SELECTOR followed by an optional explicit value, and then a message 441 if(MessagePattern::Part::hasNumericValue(pattern.getPartType(partIndex))) { 442 // explicit value like "=2" 443 part=&pattern.getPart(partIndex++); 444 if(number==pattern.getNumericValue(*part)) { 445 // matches explicit value 446 return partIndex; 447 } 448 } else if(!haveKeywordMatch) { 449 // plural keyword like "few" or "other" 450 // Compare "other" first and call the selector if this is not "other". 451 if(pattern.partSubstringMatches(*part, other)) { 452 if(msgStart==0) { 453 msgStart=partIndex; 454 if(0 == keyword.compare(other)) { 455 // This is the first "other" sub-message, 456 // and the selected keyword is also "other". 457 // Do not match "other" again. 458 haveKeywordMatch=TRUE; 459 } 460 } 461 } else { 462 if(keyword.isEmpty()) { 463 keyword=selector.select(context, number-offset, ec); 464 if(msgStart!=0 && (0 == keyword.compare(other))) { 465 // We have already seen an "other" sub-message. 466 // Do not match "other" again. 467 haveKeywordMatch=TRUE; 468 // Skip keyword matching but do getLimitPartIndex(). 469 } 470 } 471 if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) { 472 // keyword matches 473 msgStart=partIndex; 474 // Do not match this keyword again. 475 haveKeywordMatch=TRUE; 476 } 477 } 478 } 479 partIndex=pattern.getLimitPartIndex(partIndex); 480 } while(++partIndex<count); 481 return msgStart; 482} 483 484PluralFormat::PluralSelector::~PluralSelector() {} 485 486PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() { 487 delete pluralRules; 488} 489 490UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number, 491 UErrorCode& /*ec*/) const { 492 (void)number; // unused except in the assertion 493 FixedDecimal *dec=static_cast<FixedDecimal *>(context); 494 U_ASSERT(dec->source==number); 495 return pluralRules->select(*dec); 496} 497 498void PluralFormat::PluralSelectorAdapter::reset() { 499 delete pluralRules; 500 pluralRules = NULL; 501} 502 503 504U_NAMESPACE_END 505 506 507#endif /* #if !UCONFIG_NO_FORMATTING */ 508 509//eof 510