1 /* 2 ****************************************************************************** 3 * Copyright (C) 2014-2015, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ****************************************************************************** 6 * 7 * File reldatefmt.cpp 8 ****************************************************************************** 9 */ 10 11 #include "unicode/reldatefmt.h" 12 13 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION 14 15 #include "unicode/localpointer.h" 16 #include "quantityformatter.h" 17 #include "unicode/plurrule.h" 18 #include "unicode/msgfmt.h" 19 #include "unicode/decimfmt.h" 20 #include "unicode/numfmt.h" 21 #include "unicode/brkiter.h" 22 #include "uresimp.h" 23 #include "unicode/ures.h" 24 #include "cstring.h" 25 #include "ucln_in.h" 26 #include "mutex.h" 27 #include "charstr.h" 28 #include "uassert.h" 29 30 #include "sharedbreakiterator.h" 31 #include "sharedpluralrules.h" 32 #include "sharednumberformat.h" 33 #include "unifiedcache.h" 34 35 // Copied from uscript_props.cpp 36 37 static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER; 38 39 U_NAMESPACE_BEGIN 40 41 // RelativeDateTimeFormatter specific data for a single locale 42 class RelativeDateTimeCacheData: public SharedObject { 43 public: 44 RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { } 45 virtual ~RelativeDateTimeCacheData(); 46 47 // no numbers: e.g Next Tuesday; Yesterday; etc. 48 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT]; 49 50 // has numbers: e.g Next Tuesday; Yesterday; etc. For second index, 0 51 // means past e.g 5 days ago; 1 means future e.g in 5 days. 52 QuantityFormatter relativeUnits[UDAT_STYLE_COUNT][UDAT_RELATIVE_UNIT_COUNT][2]; 53 54 void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) { 55 delete combinedDateAndTime; 56 combinedDateAndTime = mfToAdopt; 57 } 58 const MessageFormat *getCombinedDateAndTime() const { 59 return combinedDateAndTime; 60 } 61 private: 62 MessageFormat *combinedDateAndTime; 63 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other); 64 RelativeDateTimeCacheData& operator=( 65 const RelativeDateTimeCacheData &other); 66 }; 67 68 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() { 69 delete combinedDateAndTime; 70 } 71 72 static UBool getStringWithFallback( 73 const UResourceBundle *resource, 74 const char *key, 75 UnicodeString &result, 76 UErrorCode &status) { 77 int32_t len = 0; 78 const UChar *resStr = ures_getStringByKeyWithFallback( 79 resource, key, &len, &status); 80 if (U_FAILURE(status)) { 81 return FALSE; 82 } 83 result.setTo(TRUE, resStr, len); 84 return TRUE; 85 } 86 87 static UBool getOptionalStringWithFallback( 88 const UResourceBundle *resource, 89 const char *key, 90 UnicodeString &result, 91 UErrorCode &status) { 92 if (U_FAILURE(status)) { 93 return FALSE; 94 } 95 int32_t len = 0; 96 const UChar *resStr = ures_getStringByKey( 97 resource, key, &len, &status); 98 if (status == U_MISSING_RESOURCE_ERROR) { 99 result.remove(); 100 status = U_ZERO_ERROR; 101 return TRUE; 102 } 103 if (U_FAILURE(status)) { 104 return FALSE; 105 } 106 result.setTo(TRUE, resStr, len); 107 return TRUE; 108 } 109 110 static UBool getString( 111 const UResourceBundle *resource, 112 UnicodeString &result, 113 UErrorCode &status) { 114 int32_t len = 0; 115 const UChar *resStr = ures_getString(resource, &len, &status); 116 if (U_FAILURE(status)) { 117 return FALSE; 118 } 119 result.setTo(TRUE, resStr, len); 120 return TRUE; 121 } 122 123 static UBool getStringByIndex( 124 const UResourceBundle *resource, 125 int32_t idx, 126 UnicodeString &result, 127 UErrorCode &status) { 128 int32_t len = 0; 129 const UChar *resStr = ures_getStringByIndex( 130 resource, idx, &len, &status); 131 if (U_FAILURE(status)) { 132 return FALSE; 133 } 134 result.setTo(TRUE, resStr, len); 135 return TRUE; 136 } 137 138 static void initAbsoluteUnit( 139 const UResourceBundle *resource, 140 const UnicodeString &unitName, 141 UnicodeString *absoluteUnit, 142 UErrorCode &status) { 143 getStringWithFallback( 144 resource, 145 "-1", 146 absoluteUnit[UDAT_DIRECTION_LAST], 147 status); 148 getStringWithFallback( 149 resource, 150 "0", 151 absoluteUnit[UDAT_DIRECTION_THIS], 152 status); 153 getStringWithFallback( 154 resource, 155 "1", 156 absoluteUnit[UDAT_DIRECTION_NEXT], 157 status); 158 getOptionalStringWithFallback( 159 resource, 160 "-2", 161 absoluteUnit[UDAT_DIRECTION_LAST_2], 162 status); 163 getOptionalStringWithFallback( 164 resource, 165 "2", 166 absoluteUnit[UDAT_DIRECTION_NEXT_2], 167 status); 168 absoluteUnit[UDAT_DIRECTION_PLAIN] = unitName; 169 } 170 171 static void initQuantityFormatter( 172 const UResourceBundle *resource, 173 QuantityFormatter &formatter, 174 UErrorCode &status) { 175 if (U_FAILURE(status)) { 176 return; 177 } 178 int32_t size = ures_getSize(resource); 179 for (int32_t i = 0; i < size; ++i) { 180 LocalUResourceBundlePointer pluralBundle( 181 ures_getByIndex(resource, i, NULL, &status)); 182 if (U_FAILURE(status)) { 183 return; 184 } 185 UnicodeString rawPattern; 186 if (!getString(pluralBundle.getAlias(), rawPattern, status)) { 187 return; 188 } 189 if (!formatter.addIfAbsent( 190 ures_getKey(pluralBundle.getAlias()), 191 rawPattern, 192 status)) { 193 return; 194 } 195 } 196 } 197 198 static void initRelativeUnit( 199 const UResourceBundle *resource, 200 QuantityFormatter *relativeUnit, 201 UErrorCode &status) { 202 LocalUResourceBundlePointer topLevel( 203 ures_getByKeyWithFallback( 204 resource, "relativeTime", NULL, &status)); 205 if (U_FAILURE(status)) { 206 return; 207 } 208 LocalUResourceBundlePointer futureBundle(ures_getByKeyWithFallback( 209 topLevel.getAlias(), "future", NULL, &status)); 210 if (U_FAILURE(status)) { 211 return; 212 } 213 initQuantityFormatter( 214 futureBundle.getAlias(), 215 relativeUnit[1], 216 status); 217 LocalUResourceBundlePointer pastBundle(ures_getByKeyWithFallback( 218 topLevel.getAlias(), "past", NULL, &status)); 219 if (U_FAILURE(status)) { 220 return; 221 } 222 initQuantityFormatter( 223 pastBundle.getAlias(), 224 relativeUnit[0], 225 status); 226 } 227 228 static void initRelativeUnit( 229 const UResourceBundle *resource, 230 const char *path, 231 QuantityFormatter *relativeUnit, 232 UErrorCode &status) { 233 LocalUResourceBundlePointer topLevel( 234 ures_getByKeyWithFallback(resource, path, NULL, &status)); 235 if (U_FAILURE(status)) { 236 return; 237 } 238 initRelativeUnit(topLevel.getAlias(), relativeUnit, status); 239 } 240 241 static void addTimeUnit( 242 const UResourceBundle *resource, 243 const char *path, 244 QuantityFormatter *relativeUnit, 245 UnicodeString *absoluteUnit, 246 UErrorCode &status) { 247 LocalUResourceBundlePointer topLevel( 248 ures_getByKeyWithFallback(resource, path, NULL, &status)); 249 if (U_FAILURE(status)) { 250 return; 251 } 252 initRelativeUnit(topLevel.getAlias(), relativeUnit, status); 253 UnicodeString unitName; 254 if (!getStringWithFallback(topLevel.getAlias(), "dn", unitName, status)) { 255 return; 256 } 257 // TODO(Travis Keep): This is a hack to get around CLDR bug 6818. 258 const char *localeId = ures_getLocaleByType( 259 topLevel.getAlias(), ULOC_ACTUAL_LOCALE, &status); 260 if (U_FAILURE(status)) { 261 return; 262 } 263 Locale locale(localeId); 264 if (uprv_strcmp("en", locale.getLanguage()) == 0) { 265 unitName.toLower(); 266 } 267 // end hack 268 ures_getByKeyWithFallback( 269 topLevel.getAlias(), "relative", topLevel.getAlias(), &status); 270 if (U_FAILURE(status)) { 271 return; 272 } 273 initAbsoluteUnit( 274 topLevel.getAlias(), 275 unitName, 276 absoluteUnit, 277 status); 278 } 279 280 static void readDaysOfWeek( 281 const UResourceBundle *resource, 282 const char *path, 283 UnicodeString *daysOfWeek, 284 UErrorCode &status) { 285 LocalUResourceBundlePointer topLevel( 286 ures_getByKeyWithFallback(resource, path, NULL, &status)); 287 if (U_FAILURE(status)) { 288 return; 289 } 290 int32_t size = ures_getSize(topLevel.getAlias()); 291 if (size != 7) { 292 status = U_INTERNAL_PROGRAM_ERROR; 293 return; 294 } 295 for (int32_t i = 0; i < size; ++i) { 296 if (!getStringByIndex(topLevel.getAlias(), i, daysOfWeek[i], status)) { 297 return; 298 } 299 } 300 } 301 302 static void addWeekDay( 303 const UResourceBundle *resource, 304 const char *path, 305 const UnicodeString *daysOfWeek, 306 UDateAbsoluteUnit absoluteUnit, 307 UnicodeString absoluteUnits[][UDAT_DIRECTION_COUNT], 308 UErrorCode &status) { 309 LocalUResourceBundlePointer topLevel( 310 ures_getByKeyWithFallback(resource, path, NULL, &status)); 311 if (U_FAILURE(status)) { 312 return; 313 } 314 initAbsoluteUnit( 315 topLevel.getAlias(), 316 daysOfWeek[absoluteUnit - UDAT_ABSOLUTE_SUNDAY], 317 absoluteUnits[absoluteUnit], 318 status); 319 } 320 321 static void addTimeUnits( 322 const UResourceBundle *resource, 323 const char *path, const char *pathShort, const char *pathNarrow, 324 UDateRelativeUnit relativeUnit, 325 UDateAbsoluteUnit absoluteUnit, 326 RelativeDateTimeCacheData &cacheData, 327 UErrorCode &status) { 328 addTimeUnit( 329 resource, 330 path, 331 cacheData.relativeUnits[UDAT_STYLE_LONG][relativeUnit], 332 cacheData.absoluteUnits[UDAT_STYLE_LONG][absoluteUnit], 333 status); 334 addTimeUnit( 335 resource, 336 pathShort, 337 cacheData.relativeUnits[UDAT_STYLE_SHORT][relativeUnit], 338 cacheData.absoluteUnits[UDAT_STYLE_SHORT][absoluteUnit], 339 status); 340 if (U_FAILURE(status)) { 341 return; 342 } 343 addTimeUnit( 344 resource, 345 pathNarrow, 346 cacheData.relativeUnits[UDAT_STYLE_NARROW][relativeUnit], 347 cacheData.absoluteUnits[UDAT_STYLE_NARROW][absoluteUnit], 348 status); 349 if (status == U_MISSING_RESOURCE_ERROR) { 350 // retry addTimeUnit for UDAT_STYLE_NARROW using pathShort 351 status = U_ZERO_ERROR; 352 addTimeUnit( 353 resource, 354 pathShort, 355 cacheData.relativeUnits[UDAT_STYLE_NARROW][relativeUnit], 356 cacheData.absoluteUnits[UDAT_STYLE_NARROW][absoluteUnit], 357 status); 358 } 359 } 360 361 static void initRelativeUnits( 362 const UResourceBundle *resource, 363 const char *path, const char *pathShort, const char *pathNarrow, 364 UDateRelativeUnit relativeUnit, 365 QuantityFormatter relativeUnits[][UDAT_RELATIVE_UNIT_COUNT][2], 366 UErrorCode &status) { 367 initRelativeUnit( 368 resource, 369 path, 370 relativeUnits[UDAT_STYLE_LONG][relativeUnit], 371 status); 372 initRelativeUnit( 373 resource, 374 pathShort, 375 relativeUnits[UDAT_STYLE_SHORT][relativeUnit], 376 status); 377 if (U_FAILURE(status)) { 378 return; 379 } 380 initRelativeUnit( 381 resource, 382 pathNarrow, 383 relativeUnits[UDAT_STYLE_NARROW][relativeUnit], 384 status); 385 if (status == U_MISSING_RESOURCE_ERROR) { 386 // retry initRelativeUnit for UDAT_STYLE_NARROW using pathShort 387 status = U_ZERO_ERROR; 388 initRelativeUnit( 389 resource, 390 pathShort, 391 relativeUnits[UDAT_STYLE_NARROW][relativeUnit], 392 status); 393 } 394 } 395 396 static void addWeekDays( 397 const UResourceBundle *resource, 398 const char *path, const char *pathShort, const char *pathNarrow, 399 const UnicodeString daysOfWeek[][7], 400 UDateAbsoluteUnit absoluteUnit, 401 UnicodeString absoluteUnits[][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT], 402 UErrorCode &status) { 403 addWeekDay( 404 resource, 405 path, 406 daysOfWeek[UDAT_STYLE_LONG], 407 absoluteUnit, 408 absoluteUnits[UDAT_STYLE_LONG], 409 status); 410 addWeekDay( 411 resource, 412 pathShort, 413 daysOfWeek[UDAT_STYLE_SHORT], 414 absoluteUnit, 415 absoluteUnits[UDAT_STYLE_SHORT], 416 status); 417 if (U_FAILURE(status)) { 418 return; 419 } 420 addWeekDay( 421 resource, 422 pathNarrow, 423 daysOfWeek[UDAT_STYLE_NARROW], 424 absoluteUnit, 425 absoluteUnits[UDAT_STYLE_NARROW], 426 status); 427 if (status == U_MISSING_RESOURCE_ERROR) { 428 // retry addWeekDay for UDAT_STYLE_NARROW using pathShort 429 status = U_ZERO_ERROR; 430 addWeekDay( 431 resource, 432 pathShort, 433 daysOfWeek[UDAT_STYLE_NARROW], 434 absoluteUnit, 435 absoluteUnits[UDAT_STYLE_NARROW], 436 status); 437 } 438 } 439 440 static UBool loadUnitData( 441 const UResourceBundle *resource, 442 RelativeDateTimeCacheData &cacheData, 443 UErrorCode &status) { 444 addTimeUnits( 445 resource, 446 "fields/day", "fields/day-short", "fields/day-narrow", 447 UDAT_RELATIVE_DAYS, 448 UDAT_ABSOLUTE_DAY, 449 cacheData, 450 status); 451 addTimeUnits( 452 resource, 453 "fields/week", "fields/week-short", "fields/week-narrow", 454 UDAT_RELATIVE_WEEKS, 455 UDAT_ABSOLUTE_WEEK, 456 cacheData, 457 status); 458 addTimeUnits( 459 resource, 460 "fields/month", "fields/month-short", "fields/month-narrow", 461 UDAT_RELATIVE_MONTHS, 462 UDAT_ABSOLUTE_MONTH, 463 cacheData, 464 status); 465 addTimeUnits( 466 resource, 467 "fields/year", "fields/year-short", "fields/year-narrow", 468 UDAT_RELATIVE_YEARS, 469 UDAT_ABSOLUTE_YEAR, 470 cacheData, 471 status); 472 initRelativeUnits( 473 resource, 474 "fields/second", "fields/second-short", "fields/second-narrow", 475 UDAT_RELATIVE_SECONDS, 476 cacheData.relativeUnits, 477 status); 478 initRelativeUnits( 479 resource, 480 "fields/minute", "fields/minute-short", "fields/minute-narrow", 481 UDAT_RELATIVE_MINUTES, 482 cacheData.relativeUnits, 483 status); 484 initRelativeUnits( 485 resource, 486 "fields/hour", "fields/hour-short", "fields/hour-narrow", 487 UDAT_RELATIVE_HOURS, 488 cacheData.relativeUnits, 489 status); 490 getStringWithFallback( 491 resource, 492 "fields/second/relative/0", 493 cacheData.absoluteUnits[UDAT_STYLE_LONG][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN], 494 status); 495 getStringWithFallback( 496 resource, 497 "fields/second-short/relative/0", 498 cacheData.absoluteUnits[UDAT_STYLE_SHORT][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN], 499 status); 500 getStringWithFallback( 501 resource, 502 "fields/second-narrow/relative/0", 503 cacheData.absoluteUnits[UDAT_STYLE_NARROW][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN], 504 status); 505 UnicodeString daysOfWeek[UDAT_STYLE_COUNT][7]; 506 readDaysOfWeek( 507 resource, 508 "calendar/gregorian/dayNames/stand-alone/wide", 509 daysOfWeek[UDAT_STYLE_LONG], 510 status); 511 readDaysOfWeek( 512 resource, 513 "calendar/gregorian/dayNames/stand-alone/short", 514 daysOfWeek[UDAT_STYLE_SHORT], 515 status); 516 readDaysOfWeek( 517 resource, 518 "calendar/gregorian/dayNames/stand-alone/narrow", 519 daysOfWeek[UDAT_STYLE_NARROW], 520 status); 521 addWeekDays( 522 resource, 523 "fields/mon/relative", 524 "fields/mon-short/relative", 525 "fields/mon-narrow/relative", 526 daysOfWeek, 527 UDAT_ABSOLUTE_MONDAY, 528 cacheData.absoluteUnits, 529 status); 530 addWeekDays( 531 resource, 532 "fields/tue/relative", 533 "fields/tue-short/relative", 534 "fields/tue-narrow/relative", 535 daysOfWeek, 536 UDAT_ABSOLUTE_TUESDAY, 537 cacheData.absoluteUnits, 538 status); 539 addWeekDays( 540 resource, 541 "fields/wed/relative", 542 "fields/wed-short/relative", 543 "fields/wed-narrow/relative", 544 daysOfWeek, 545 UDAT_ABSOLUTE_WEDNESDAY, 546 cacheData.absoluteUnits, 547 status); 548 addWeekDays( 549 resource, 550 "fields/thu/relative", 551 "fields/thu-short/relative", 552 "fields/thu-narrow/relative", 553 daysOfWeek, 554 UDAT_ABSOLUTE_THURSDAY, 555 cacheData.absoluteUnits, 556 status); 557 addWeekDays( 558 resource, 559 "fields/fri/relative", 560 "fields/fri-short/relative", 561 "fields/fri-narrow/relative", 562 daysOfWeek, 563 UDAT_ABSOLUTE_FRIDAY, 564 cacheData.absoluteUnits, 565 status); 566 addWeekDays( 567 resource, 568 "fields/sat/relative", 569 "fields/sat-short/relative", 570 "fields/sat-narrow/relative", 571 daysOfWeek, 572 UDAT_ABSOLUTE_SATURDAY, 573 cacheData.absoluteUnits, 574 status); 575 addWeekDays( 576 resource, 577 "fields/sun/relative", 578 "fields/sun-short/relative", 579 "fields/sun-narrow/relative", 580 daysOfWeek, 581 UDAT_ABSOLUTE_SUNDAY, 582 cacheData.absoluteUnits, 583 status); 584 return U_SUCCESS(status); 585 } 586 587 static UBool getDateTimePattern( 588 const UResourceBundle *resource, 589 UnicodeString &result, 590 UErrorCode &status) { 591 UnicodeString defaultCalendarName; 592 if (!getStringWithFallback( 593 resource, 594 "calendar/default", 595 defaultCalendarName, 596 status)) { 597 return FALSE; 598 } 599 CharString pathBuffer; 600 pathBuffer.append("calendar/", status) 601 .appendInvariantChars(defaultCalendarName, status) 602 .append("/DateTimePatterns", status); 603 LocalUResourceBundlePointer topLevel( 604 ures_getByKeyWithFallback( 605 resource, pathBuffer.data(), NULL, &status)); 606 if (U_FAILURE(status)) { 607 return FALSE; 608 } 609 int32_t size = ures_getSize(topLevel.getAlias()); 610 if (size <= 8) { 611 // Oops, size is to small to access the index that we want, fallback 612 // to a hard-coded value. 613 result = UNICODE_STRING_SIMPLE("{1} {0}"); 614 return TRUE; 615 } 616 return getStringByIndex(topLevel.getAlias(), 8, result, status); 617 } 618 619 template<> U_I18N_API 620 const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const { 621 const char *localeId = fLoc.getName(); 622 LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status)); 623 if (U_FAILURE(status)) { 624 return NULL; 625 } 626 LocalPointer<RelativeDateTimeCacheData> result( 627 new RelativeDateTimeCacheData()); 628 if (result.isNull()) { 629 status = U_MEMORY_ALLOCATION_ERROR; 630 return NULL; 631 } 632 if (!loadUnitData( 633 topLevel.getAlias(), 634 *result, 635 status)) { 636 return NULL; 637 } 638 UnicodeString dateTimePattern; 639 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) { 640 return NULL; 641 } 642 result->adoptCombinedDateAndTime( 643 new MessageFormat(dateTimePattern, localeId, status)); 644 if (U_FAILURE(status)) { 645 return NULL; 646 } 647 result->addRef(); 648 return result.orphan(); 649 } 650 651 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) : 652 fCache(NULL), 653 fNumberFormat(NULL), 654 fPluralRules(NULL), 655 fStyle(UDAT_STYLE_LONG), 656 fContext(UDISPCTX_CAPITALIZATION_NONE), 657 fOptBreakIterator(NULL) { 658 init(NULL, NULL, status); 659 } 660 661 RelativeDateTimeFormatter::RelativeDateTimeFormatter( 662 const Locale& locale, UErrorCode& status) : 663 fCache(NULL), 664 fNumberFormat(NULL), 665 fPluralRules(NULL), 666 fStyle(UDAT_STYLE_LONG), 667 fContext(UDISPCTX_CAPITALIZATION_NONE), 668 fOptBreakIterator(NULL), 669 fLocale(locale) { 670 init(NULL, NULL, status); 671 } 672 673 RelativeDateTimeFormatter::RelativeDateTimeFormatter( 674 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) : 675 fCache(NULL), 676 fNumberFormat(NULL), 677 fPluralRules(NULL), 678 fStyle(UDAT_STYLE_LONG), 679 fContext(UDISPCTX_CAPITALIZATION_NONE), 680 fOptBreakIterator(NULL), 681 fLocale(locale) { 682 init(nfToAdopt, NULL, status); 683 } 684 685 RelativeDateTimeFormatter::RelativeDateTimeFormatter( 686 const Locale& locale, 687 NumberFormat *nfToAdopt, 688 UDateRelativeDateTimeFormatterStyle styl, 689 UDisplayContext capitalizationContext, 690 UErrorCode& status) : 691 fCache(NULL), 692 fNumberFormat(NULL), 693 fPluralRules(NULL), 694 fStyle(styl), 695 fContext(capitalizationContext), 696 fOptBreakIterator(NULL), 697 fLocale(locale) { 698 if (U_FAILURE(status)) { 699 return; 700 } 701 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) { 702 status = U_ILLEGAL_ARGUMENT_ERROR; 703 return; 704 } 705 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { 706 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status); 707 if (U_FAILURE(status)) { 708 return; 709 } 710 init(nfToAdopt, bi, status); 711 } else { 712 init(nfToAdopt, NULL, status); 713 } 714 } 715 716 RelativeDateTimeFormatter::RelativeDateTimeFormatter( 717 const RelativeDateTimeFormatter& other) 718 : UObject(other), 719 fCache(other.fCache), 720 fNumberFormat(other.fNumberFormat), 721 fPluralRules(other.fPluralRules), 722 fStyle(other.fStyle), 723 fContext(other.fContext), 724 fOptBreakIterator(other.fOptBreakIterator), 725 fLocale(other.fLocale) { 726 fCache->addRef(); 727 fNumberFormat->addRef(); 728 fPluralRules->addRef(); 729 if (fOptBreakIterator != NULL) { 730 fOptBreakIterator->addRef(); 731 } 732 } 733 734 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=( 735 const RelativeDateTimeFormatter& other) { 736 if (this != &other) { 737 SharedObject::copyPtr(other.fCache, fCache); 738 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat); 739 SharedObject::copyPtr(other.fPluralRules, fPluralRules); 740 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator); 741 fStyle = other.fStyle; 742 fContext = other.fContext; 743 fLocale = other.fLocale; 744 } 745 return *this; 746 } 747 748 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() { 749 if (fCache != NULL) { 750 fCache->removeRef(); 751 } 752 if (fNumberFormat != NULL) { 753 fNumberFormat->removeRef(); 754 } 755 if (fPluralRules != NULL) { 756 fPluralRules->removeRef(); 757 } 758 if (fOptBreakIterator != NULL) { 759 fOptBreakIterator->removeRef(); 760 } 761 } 762 763 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const { 764 return **fNumberFormat; 765 } 766 767 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const { 768 return fContext; 769 } 770 771 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const { 772 return fStyle; 773 } 774 775 UnicodeString& RelativeDateTimeFormatter::format( 776 double quantity, UDateDirection direction, UDateRelativeUnit unit, 777 UnicodeString& appendTo, UErrorCode& status) const { 778 if (U_FAILURE(status)) { 779 return appendTo; 780 } 781 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) { 782 status = U_ILLEGAL_ARGUMENT_ERROR; 783 return appendTo; 784 } 785 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; 786 FieldPosition pos(FieldPosition::DONT_CARE); 787 if (fOptBreakIterator == NULL) { 788 return fCache->relativeUnits[fStyle][unit][bFuture].format( 789 quantity, 790 **fNumberFormat, 791 **fPluralRules, 792 appendTo, 793 pos, 794 status); 795 } 796 UnicodeString result; 797 fCache->relativeUnits[fStyle][unit][bFuture].format( 798 quantity, 799 **fNumberFormat, 800 **fPluralRules, 801 result, 802 pos, 803 status); 804 adjustForContext(result); 805 return appendTo.append(result); 806 } 807 808 UnicodeString& RelativeDateTimeFormatter::format( 809 UDateDirection direction, UDateAbsoluteUnit unit, 810 UnicodeString& appendTo, UErrorCode& status) const { 811 if (U_FAILURE(status)) { 812 return appendTo; 813 } 814 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) { 815 status = U_ILLEGAL_ARGUMENT_ERROR; 816 return appendTo; 817 } 818 if (fOptBreakIterator == NULL) { 819 return appendTo.append(fCache->absoluteUnits[fStyle][unit][direction]); 820 } 821 UnicodeString result(fCache->absoluteUnits[fStyle][unit][direction]); 822 adjustForContext(result); 823 return appendTo.append(result); 824 } 825 826 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime( 827 const UnicodeString& relativeDateString, const UnicodeString& timeString, 828 UnicodeString& appendTo, UErrorCode& status) const { 829 Formattable args[2] = {timeString, relativeDateString}; 830 FieldPosition fpos(0); 831 return fCache->getCombinedDateAndTime()->format( 832 args, 2, appendTo, fpos, status); 833 } 834 835 void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const { 836 if (fOptBreakIterator == NULL 837 || str.length() == 0 || !u_islower(str.char32At(0))) { 838 return; 839 } 840 841 // Must guarantee that one thread at a time accesses the shared break 842 // iterator. 843 Mutex lock(&gBrkIterMutex); 844 str.toTitle( 845 fOptBreakIterator->get(), 846 fLocale, 847 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); 848 } 849 850 void RelativeDateTimeFormatter::init( 851 NumberFormat *nfToAdopt, 852 BreakIterator *biToAdopt, 853 UErrorCode &status) { 854 LocalPointer<NumberFormat> nf(nfToAdopt); 855 LocalPointer<BreakIterator> bi(biToAdopt); 856 UnifiedCache::getByLocale(fLocale, fCache, status); 857 if (U_FAILURE(status)) { 858 return; 859 } 860 const SharedPluralRules *pr = PluralRules::createSharedInstance( 861 fLocale, UPLURAL_TYPE_CARDINAL, status); 862 if (U_FAILURE(status)) { 863 return; 864 } 865 SharedObject::copyPtr(pr, fPluralRules); 866 pr->removeRef(); 867 if (nf.isNull()) { 868 const SharedNumberFormat *shared = NumberFormat::createSharedInstance( 869 fLocale, UNUM_DECIMAL, status); 870 if (U_FAILURE(status)) { 871 return; 872 } 873 SharedObject::copyPtr(shared, fNumberFormat); 874 shared->removeRef(); 875 } else { 876 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); 877 if (shared == NULL) { 878 status = U_MEMORY_ALLOCATION_ERROR; 879 return; 880 } 881 nf.orphan(); 882 SharedObject::copyPtr(shared, fNumberFormat); 883 } 884 if (bi.isNull()) { 885 SharedObject::clearPtr(fOptBreakIterator); 886 } else { 887 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias()); 888 if (shared == NULL) { 889 status = U_MEMORY_ALLOCATION_ERROR; 890 return; 891 } 892 bi.orphan(); 893 SharedObject::copyPtr(shared, fOptBreakIterator); 894 } 895 } 896 897 898 U_NAMESPACE_END 899 900 #endif /* !UCONFIG_NO_FORMATTING */ 901 902