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:
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.addIfAbsent(
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