1 /* 2 ******************************************************************************* 3 * Copyright (C) 2009, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ******************************************************************************* 6 * 7 * File PLURFMT.CPP 8 * 9 * Modification History: 10 * 11 * Date Name Description 12 ******************************************************************************* 13 */ 14 15 16 #include "unicode/utypes.h" 17 #include "unicode/plurfmt.h" 18 #include "unicode/plurrule.h" 19 #include "plurrule_impl.h" 20 21 #if !UCONFIG_NO_FORMATTING 22 23 U_NAMESPACE_BEGIN 24 25 U_CDECL_BEGIN 26 static void U_CALLCONV 27 deleteHashStrings(void *obj) { 28 delete (UnicodeString *)obj; 29 } 30 U_CDECL_END 31 32 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat) 33 34 #define MAX_KEYWORD_SIZE 30 35 36 PluralFormat::PluralFormat(UErrorCode& status) { 37 init(NULL, Locale::getDefault(), status); 38 } 39 40 PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) { 41 init(NULL, loc, status); 42 } 43 44 PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) { 45 init(&rules, Locale::getDefault(), status); 46 } 47 48 PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, UErrorCode& status) { 49 init(&rules, loc, status); 50 } 51 52 PluralFormat::PluralFormat(const UnicodeString& pat, UErrorCode& status) { 53 init(NULL, Locale::getDefault(), status); 54 applyPattern(pat, status); 55 } 56 57 PluralFormat::PluralFormat(const Locale& loc, const UnicodeString& pat, UErrorCode& status) { 58 init(NULL, loc, status); 59 applyPattern(pat, status); 60 } 61 62 PluralFormat::PluralFormat(const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) { 63 init(&rules, Locale::getDefault(), status); 64 applyPattern(pat, status); 65 } 66 67 PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) { 68 init(&rules, loc, status); 69 applyPattern(pat, status); 70 } 71 72 PluralFormat::PluralFormat(const PluralFormat& other) : Format(other) { 73 UErrorCode status = U_ZERO_ERROR; 74 locale = other.locale; 75 pluralRules = other.pluralRules->clone(); 76 pattern = other.pattern; 77 copyHashtable(other.fParsedValuesHash, status); 78 if (U_FAILURE(status)) { 79 delete pluralRules; 80 pluralRules = NULL; 81 return; 82 } 83 numberFormat=NumberFormat::createInstance(locale, status); 84 if (U_FAILURE(status)) { 85 delete pluralRules; 86 pluralRules = NULL; 87 delete fParsedValuesHash; 88 fParsedValuesHash = NULL; 89 return; 90 } 91 replacedNumberFormat=other.replacedNumberFormat; 92 } 93 94 PluralFormat::~PluralFormat() { 95 delete pluralRules; 96 delete fParsedValuesHash; 97 delete numberFormat; 98 } 99 100 void 101 PluralFormat::init(const PluralRules* rules, const Locale& curLocale, UErrorCode& status) { 102 if (U_FAILURE(status)) { 103 return; 104 } 105 locale = curLocale; 106 if ( rules==NULL) { 107 pluralRules = PluralRules::forLocale(locale, status); 108 if (U_FAILURE(status)) { 109 return; 110 } 111 } 112 else { 113 pluralRules = rules->clone(); 114 } 115 fParsedValuesHash=NULL; 116 pattern.remove(); 117 numberFormat= NumberFormat::createInstance(curLocale, status); 118 if (U_FAILURE(status)) { 119 delete pluralRules; 120 pluralRules = NULL; 121 return; 122 } 123 replacedNumberFormat=NULL; 124 } 125 126 void 127 PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { 128 if (U_FAILURE(status)) { 129 return; 130 } 131 this->pattern = newPattern; 132 UnicodeString token; 133 int32_t braceCount=0; 134 fmtToken type; 135 UBool spaceIncluded=FALSE; 136 137 if (fParsedValuesHash==NULL) { 138 fParsedValuesHash = new Hashtable(TRUE, status); 139 if (U_FAILURE(status)) { 140 return; 141 } 142 fParsedValuesHash->setValueDeleter(deleteHashStrings); 143 } 144 145 UBool getKeyword=TRUE; 146 UnicodeString hashKeyword; 147 UnicodeString *hashPattern; 148 149 for (int32_t i=0; i<pattern.length(); ++i) { 150 UChar ch=pattern.charAt(i); 151 152 if ( !inRange(ch, type) ) { 153 if (getKeyword) { 154 status = U_ILLEGAL_CHARACTER; 155 return; 156 } 157 else { 158 token += ch; 159 continue; 160 } 161 } 162 switch (type) { 163 case tSpace: 164 if (token.length()==0) { 165 continue; 166 } 167 if (getKeyword) { 168 // space after keyword 169 spaceIncluded = TRUE; 170 } 171 else { 172 token += ch; 173 } 174 break; 175 case tLeftBrace: 176 if ( getKeyword ) { 177 if (fParsedValuesHash->get(token)!= NULL) { 178 status = U_DUPLICATE_KEYWORD; 179 return; 180 } 181 if (token.length()==0) { 182 status = U_PATTERN_SYNTAX_ERROR; 183 return; 184 } 185 if (!pluralRules->isKeyword(token)) { 186 status = U_UNDEFINED_KEYWORD; 187 return; 188 } 189 hashKeyword = token; 190 getKeyword = FALSE; 191 token.remove(); 192 } 193 else { 194 if (braceCount==0) { 195 status = U_UNEXPECTED_TOKEN; 196 return; 197 } 198 else { 199 token += ch; 200 } 201 } 202 braceCount++; 203 spaceIncluded = FALSE; 204 break; 205 case tRightBrace: 206 if ( getKeyword ) { 207 status = U_UNEXPECTED_TOKEN; 208 return; 209 } 210 else { 211 hashPattern = new UnicodeString(token); 212 fParsedValuesHash->put(hashKeyword, hashPattern, status); 213 if (U_FAILURE(status)) { 214 return; 215 } 216 braceCount--; 217 if ( braceCount==0 ) { 218 getKeyword=TRUE; 219 hashKeyword.remove(); 220 hashPattern=NULL; 221 token.remove(); 222 } 223 else { 224 token += ch; 225 } 226 } 227 spaceIncluded = FALSE; 228 break; 229 case tLetter: 230 case tNumberSign: 231 if (spaceIncluded) { 232 status = U_PATTERN_SYNTAX_ERROR; 233 return; 234 } 235 default: 236 token+=ch; 237 break; 238 } 239 } 240 if ( checkSufficientDefinition() ) { 241 return; 242 } 243 else { 244 status = U_DEFAULT_KEYWORD_MISSING; 245 return; 246 } 247 } 248 249 UnicodeString& 250 PluralFormat::format(const Formattable& obj, 251 UnicodeString& appendTo, 252 FieldPosition& pos, 253 UErrorCode& status) const 254 { 255 if (U_FAILURE(status)) return appendTo; 256 int32_t number; 257 258 switch (obj.getType()) 259 { 260 case Formattable::kDouble: 261 return format((int32_t)obj.getDouble(), appendTo, pos, status); 262 break; 263 case Formattable::kLong: 264 number = (int32_t)obj.getLong(); 265 return format(number, appendTo, pos, status); 266 break; 267 case Formattable::kInt64: 268 return format((int32_t)obj.getInt64(), appendTo, pos, status); 269 default: 270 status = U_ILLEGAL_ARGUMENT_ERROR; 271 return appendTo; 272 } 273 } 274 275 UnicodeString 276 PluralFormat::format(int32_t number, UErrorCode& status) const { 277 if (U_FAILURE(status)) { 278 return UnicodeString(); 279 } 280 FieldPosition fpos(0); 281 UnicodeString result; 282 283 return format(number, result, fpos, status); 284 } 285 286 UnicodeString 287 PluralFormat::format(double number, UErrorCode& status) const { 288 if (U_FAILURE(status)) { 289 return UnicodeString(); 290 } 291 FieldPosition fpos(0); 292 UnicodeString result; 293 294 return format(number, result, fpos, status); 295 } 296 297 298 UnicodeString& 299 PluralFormat::format(int32_t number, 300 UnicodeString& appendTo, 301 FieldPosition& pos, 302 UErrorCode& status) const { 303 return format((double)number, appendTo, pos, status); 304 } 305 306 UnicodeString& 307 PluralFormat::format(double number, 308 UnicodeString& appendTo, 309 FieldPosition& pos, 310 UErrorCode& /*status*/) const { 311 312 if (fParsedValuesHash==NULL) { 313 if ( replacedNumberFormat== NULL ) { 314 return numberFormat->format(number, appendTo, pos); 315 } 316 else { 317 replacedNumberFormat->format(number, appendTo, pos); 318 } 319 } 320 UnicodeString selectedRule = pluralRules->select(number); 321 UnicodeString *selectedPattern = (UnicodeString *)fParsedValuesHash->get(selectedRule); 322 if (selectedPattern==NULL) { 323 selectedPattern = (UnicodeString *)fParsedValuesHash->get(pluralRules->getKeywordOther()); 324 } 325 appendTo = insertFormattedNumber(number, *selectedPattern, appendTo, pos); 326 327 return appendTo; 328 } 329 330 UnicodeString& 331 PluralFormat::toPattern(UnicodeString& appendTo) { 332 appendTo+= pattern; 333 return appendTo; 334 } 335 336 UBool 337 PluralFormat::inRange(UChar ch, fmtToken& type) { 338 if ((ch>=CAP_A) && (ch<=CAP_Z)) { 339 // we assume all characters are in lower case already. 340 return FALSE; 341 } 342 if ((ch>=LOW_A) && (ch<=LOW_Z)) { 343 type = tLetter; 344 return TRUE; 345 } 346 switch (ch) { 347 case LEFTBRACE: 348 type = tLeftBrace; 349 return TRUE; 350 case SPACE: 351 type = tSpace; 352 return TRUE; 353 case RIGHTBRACE: 354 type = tRightBrace; 355 return TRUE; 356 case NUMBER_SIGN: 357 type = tNumberSign; 358 return TRUE; 359 default : 360 type = none; 361 return FALSE; 362 } 363 } 364 365 UBool 366 PluralFormat::checkSufficientDefinition() { 367 // Check that at least the default rule is defined. 368 if (fParsedValuesHash==NULL) return FALSE; 369 if (fParsedValuesHash->get(pluralRules->getKeywordOther()) == NULL) { 370 return FALSE; 371 } 372 else { 373 return TRUE; 374 } 375 } 376 377 void 378 PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { 379 if (U_FAILURE(status)) { 380 return; 381 } 382 if (pluralRules!=NULL) { 383 delete pluralRules; 384 pluralRules=NULL; 385 } 386 if (fParsedValuesHash!= NULL) { 387 delete fParsedValuesHash; 388 fParsedValuesHash = NULL; 389 } 390 if (numberFormat!=NULL) { 391 delete numberFormat; 392 numberFormat = NULL; 393 replacedNumberFormat=NULL; 394 } 395 init(NULL, loc, status); 396 } 397 398 void 399 PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& /*status*/) { 400 // TODO: The copy constructor and assignment op of NumberFormat class are protected. 401 // create a pointer as the workaround. 402 replacedNumberFormat = (NumberFormat *)format; 403 } 404 405 Format* 406 PluralFormat::clone() const 407 { 408 return new PluralFormat(*this); 409 } 410 411 PluralFormat& 412 PluralFormat::operator=(const PluralFormat& other) { 413 if (this != &other) { 414 UErrorCode status = U_ZERO_ERROR; 415 delete pluralRules; 416 delete fParsedValuesHash; 417 delete numberFormat; 418 locale = other.locale; 419 pluralRules = other.pluralRules->clone(); 420 pattern = other.pattern; 421 copyHashtable(other.fParsedValuesHash, status); 422 if (U_FAILURE(status)) { 423 delete pluralRules; 424 pluralRules = NULL; 425 fParsedValuesHash = NULL; 426 numberFormat = NULL; 427 return *this; 428 } 429 numberFormat=NumberFormat::createInstance(locale, status); 430 if (U_FAILURE(status)) { 431 delete pluralRules; 432 delete fParsedValuesHash; 433 pluralRules = NULL; 434 fParsedValuesHash = NULL; 435 numberFormat = NULL; 436 return *this; 437 } 438 replacedNumberFormat=other.replacedNumberFormat; 439 } 440 441 return *this; 442 } 443 444 UBool 445 PluralFormat::operator==(const Format& other) const { 446 // This protected comparison operator should only be called by subclasses 447 // which have confirmed that the other object being compared against is 448 // an instance of a sublcass of PluralFormat. THIS IS IMPORTANT. 449 // Format::operator== guarantees that this cast is safe 450 PluralFormat* fmt = (PluralFormat*)&other; 451 return ((*pluralRules == *(fmt->pluralRules)) && 452 (*numberFormat == *(fmt->numberFormat))); 453 } 454 455 UBool 456 PluralFormat::operator!=(const Format& other) const { 457 return !operator==(other); 458 } 459 460 void 461 PluralFormat::parseObject(const UnicodeString& /*source*/, 462 Formattable& /*result*/, 463 ParsePosition& /*pos*/) const 464 { 465 // TODO: not yet supported in icu4j and icu4c 466 } 467 468 UnicodeString 469 PluralFormat::insertFormattedNumber(double number, 470 UnicodeString& message, 471 UnicodeString& appendTo, 472 FieldPosition& pos) const { 473 UnicodeString result; 474 int32_t braceStack=0; 475 int32_t startIndex=0; 476 477 if (message.length()==0) { 478 return result; 479 } 480 appendTo = numberFormat->format(number, appendTo, pos); 481 for(int32_t i=0; i<message.length(); ++i) { 482 switch(message.charAt(i)) { 483 case LEFTBRACE: 484 ++braceStack; 485 break; 486 case RIGHTBRACE: 487 --braceStack; 488 break; 489 case NUMBER_SIGN: 490 if (braceStack==0) { 491 result += UnicodeString(message, startIndex, i); 492 result += appendTo; 493 startIndex = i + 1; 494 } 495 break; 496 } 497 } 498 if ( startIndex < message.length() ) { 499 result += UnicodeString(message, startIndex, message.length()-startIndex); 500 } 501 appendTo = result; 502 return result; 503 } 504 505 void 506 PluralFormat::copyHashtable(Hashtable *other, UErrorCode& status) { 507 if (other == NULL || U_FAILURE(status)) { 508 fParsedValuesHash = NULL; 509 return; 510 } 511 fParsedValuesHash = new Hashtable(TRUE, status); 512 if(U_FAILURE(status)){ 513 return; 514 } 515 fParsedValuesHash->setValueDeleter(deleteHashStrings); 516 int32_t pos = -1; 517 const UHashElement* elem = NULL; 518 // walk through the hash table and create a deep clone 519 while((elem = other->nextElement(pos))!= NULL){ 520 const UHashTok otherKeyTok = elem->key; 521 UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer; 522 const UHashTok otherKeyToVal = elem->value; 523 UnicodeString* otherValue = (UnicodeString*)otherKeyToVal.pointer; 524 fParsedValuesHash->put(*otherKey, new UnicodeString(*otherValue), status); 525 if(U_FAILURE(status)){ 526 return; 527 } 528 } 529 } 530 531 532 U_NAMESPACE_END 533 534 535 #endif /* #if !UCONFIG_NO_FORMATTING */ 536 537 //eof 538