Home | History | Annotate | Download | only in i18n
      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