1 /*
2 ******************************************************************************
3 * Copyright (C) 2014, 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:
RelativeDateTimeCacheData()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 
adoptCombinedDateAndTime(MessageFormat * mfToAdopt)54     void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) {
55         delete combinedDateAndTime;
56         combinedDateAndTime = mfToAdopt;
57     }
getCombinedDateAndTime() const58     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 
~RelativeDateTimeCacheData()68 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
69     delete combinedDateAndTime;
70 }
71 
getStringWithFallback(const UResourceBundle * resource,const char * key,UnicodeString & result,UErrorCode & status)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 
getOptionalStringWithFallback(const UResourceBundle * resource,const char * key,UnicodeString & result,UErrorCode & status)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 
getString(const UResourceBundle * resource,UnicodeString & result,UErrorCode & status)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 
getStringByIndex(const UResourceBundle * resource,int32_t idx,UnicodeString & result,UErrorCode & status)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 
initAbsoluteUnit(const UResourceBundle * resource,const UnicodeString & unitName,UnicodeString * absoluteUnit,UErrorCode & status)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 
initQuantityFormatter(const UResourceBundle * resource,QuantityFormatter & formatter,UErrorCode & status)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.add(
190                 ures_getKey(pluralBundle.getAlias()),
191                 rawPattern,
192                 status)) {
193             return;
194         }
195     }
196 }
197 
initRelativeUnit(const UResourceBundle * resource,QuantityFormatter * relativeUnit,UErrorCode & status)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 
initRelativeUnit(const UResourceBundle * resource,const char * path,QuantityFormatter * relativeUnit,UErrorCode & status)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 
addTimeUnit(const UResourceBundle * resource,const char * path,QuantityFormatter * relativeUnit,UnicodeString * absoluteUnit,UErrorCode & status)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 
readDaysOfWeek(const UResourceBundle * resource,const char * path,UnicodeString * daysOfWeek,UErrorCode & status)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 
addWeekDay(const UResourceBundle * resource,const char * path,const UnicodeString * daysOfWeek,UDateAbsoluteUnit absoluteUnit,UnicodeString absoluteUnits[][UDAT_DIRECTION_COUNT],UErrorCode & status)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 
addTimeUnits(const UResourceBundle * resource,const char * path,const char * pathShort,const char * pathNarrow,UDateRelativeUnit relativeUnit,UDateAbsoluteUnit absoluteUnit,RelativeDateTimeCacheData & cacheData,UErrorCode & status)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 
initRelativeUnits(const UResourceBundle * resource,const char * path,const char * pathShort,const char * pathNarrow,UDateRelativeUnit relativeUnit,QuantityFormatter relativeUnits[][UDAT_RELATIVE_UNIT_COUNT][2],UErrorCode & status)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 
addWeekDays(const UResourceBundle * resource,const char * path,const char * pathShort,const char * pathNarrow,const UnicodeString daysOfWeek[][7],UDateAbsoluteUnit absoluteUnit,UnicodeString absoluteUnits[][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],UErrorCode & status)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 
loadUnitData(const UResourceBundle * resource,RelativeDateTimeCacheData & cacheData,UErrorCode & status)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 
getDateTimePattern(const UResourceBundle * resource,UnicodeString & result,UErrorCode & status)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
createObject(const void *,UErrorCode & status) const620 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 
RelativeDateTimeFormatter(UErrorCode & status)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 
RelativeDateTimeFormatter(const Locale & locale,UErrorCode & status)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 
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UErrorCode & status)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 
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle styl,UDisplayContext capitalizationContext,UErrorCode & status)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 
RelativeDateTimeFormatter(const RelativeDateTimeFormatter & other)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 
operator =(const RelativeDateTimeFormatter & other)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 
~RelativeDateTimeFormatter()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 
getNumberFormat() const763 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
764     return **fNumberFormat;
765 }
766 
getCapitalizationContext() const767 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
768     return fContext;
769 }
770 
getFormatStyle() const771 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
772     return fStyle;
773 }
774 
format(double quantity,UDateDirection direction,UDateRelativeUnit unit,UnicodeString & appendTo,UErrorCode & status) const775 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 
format(UDateDirection direction,UDateAbsoluteUnit unit,UnicodeString & appendTo,UErrorCode & status) const808 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 
combineDateAndTime(const UnicodeString & relativeDateString,const UnicodeString & timeString,UnicodeString & appendTo,UErrorCode & status) const826 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 
adjustForContext(UnicodeString & str) const835 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 
init(NumberFormat * nfToAdopt,BreakIterator * biToAdopt,UErrorCode & status)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