]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/reldatefmt.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / i18n / reldatefmt.cpp
CommitLineData
57a6839d
A
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
b331163b 13#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
57a6839d
A
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"
b331163b 21#include "unicode/brkiter.h"
57a6839d
A
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"
b331163b 28#include "uassert.h"
57a6839d 29
b331163b 30#include "sharedbreakiterator.h"
57a6839d
A
31#include "sharedpluralrules.h"
32#include "sharednumberformat.h"
b331163b 33#include "unifiedcache.h"
57a6839d
A
34
35// Copied from uscript_props.cpp
57a6839d 36
b331163b 37static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
57a6839d
A
38
39U_NAMESPACE_BEGIN
40
41// RelativeDateTimeFormatter specific data for a single locale
42class RelativeDateTimeCacheData: public SharedObject {
43public:
44 RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { }
45 virtual ~RelativeDateTimeCacheData();
46
47 // no numbers: e.g Next Tuesday; Yesterday; etc.
b331163b 48 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
57a6839d
A
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.
b331163b 52 QuantityFormatter relativeUnits[UDAT_STYLE_COUNT][UDAT_RELATIVE_UNIT_COUNT][2];
57a6839d
A
53
54 void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) {
55 delete combinedDateAndTime;
56 combinedDateAndTime = mfToAdopt;
57 }
58 const MessageFormat *getCombinedDateAndTime() const {
59 return combinedDateAndTime;
60 }
61private:
62 MessageFormat *combinedDateAndTime;
63 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
64 RelativeDateTimeCacheData& operator=(
65 const RelativeDateTimeCacheData &other);
66};
67
68RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
69 delete combinedDateAndTime;
70}
71
72static 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
87static 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
110static 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
123static 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
138static 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
171static 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
198static 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
228static 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
241static 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
280static 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
302static 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
b331163b 321static void addTimeUnits(
57a6839d 322 const UResourceBundle *resource,
b331163b
A
323 const char *path, const char *pathShort, const char *pathNarrow,
324 UDateRelativeUnit relativeUnit,
325 UDateAbsoluteUnit absoluteUnit,
57a6839d
A
326 RelativeDateTimeCacheData &cacheData,
327 UErrorCode &status) {
328 addTimeUnit(
b331163b
A
329 resource,
330 path,
331 cacheData.relativeUnits[UDAT_STYLE_LONG][relativeUnit],
332 cacheData.absoluteUnits[UDAT_STYLE_LONG][absoluteUnit],
333 status);
57a6839d 334 addTimeUnit(
b331163b
A
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 }
57a6839d 343 addTimeUnit(
b331163b
A
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(
57a6839d 353 resource,
b331163b
A
354 pathShort,
355 cacheData.relativeUnits[UDAT_STYLE_NARROW][relativeUnit],
356 cacheData.absoluteUnits[UDAT_STYLE_NARROW][absoluteUnit],
57a6839d 357 status);
b331163b
A
358 }
359}
360
361static 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(
57a6839d 368 resource,
b331163b
A
369 path,
370 relativeUnits[UDAT_STYLE_LONG][relativeUnit],
57a6839d
A
371 status);
372 initRelativeUnit(
373 resource,
b331163b
A
374 pathShort,
375 relativeUnits[UDAT_STYLE_SHORT][relativeUnit],
57a6839d 376 status);
b331163b
A
377 if (U_FAILURE(status)) {
378 return;
379 }
57a6839d
A
380 initRelativeUnit(
381 resource,
b331163b
A
382 pathNarrow,
383 relativeUnits[UDAT_STYLE_NARROW][relativeUnit],
57a6839d 384 status);
b331163b
A
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
396static 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
440static 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(
57a6839d 479 resource,
b331163b
A
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,
57a6839d
A
489 status);
490 getStringWithFallback(
491 resource,
492 "fields/second/relative/0",
b331163b
A
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],
57a6839d 499 status);
b331163b
A
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];
57a6839d
A
506 readDaysOfWeek(
507 resource,
508 "calendar/gregorian/dayNames/stand-alone/wide",
b331163b 509 daysOfWeek[UDAT_STYLE_LONG],
57a6839d 510 status);
b331163b
A
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(
57a6839d
A
522 resource,
523 "fields/mon/relative",
b331163b
A
524 "fields/mon-short/relative",
525 "fields/mon-narrow/relative",
57a6839d
A
526 daysOfWeek,
527 UDAT_ABSOLUTE_MONDAY,
528 cacheData.absoluteUnits,
529 status);
b331163b 530 addWeekDays(
57a6839d
A
531 resource,
532 "fields/tue/relative",
b331163b
A
533 "fields/tue-short/relative",
534 "fields/tue-narrow/relative",
57a6839d
A
535 daysOfWeek,
536 UDAT_ABSOLUTE_TUESDAY,
537 cacheData.absoluteUnits,
538 status);
b331163b 539 addWeekDays(
57a6839d
A
540 resource,
541 "fields/wed/relative",
b331163b
A
542 "fields/wed-short/relative",
543 "fields/wed-narrow/relative",
57a6839d
A
544 daysOfWeek,
545 UDAT_ABSOLUTE_WEDNESDAY,
546 cacheData.absoluteUnits,
547 status);
b331163b 548 addWeekDays(
57a6839d
A
549 resource,
550 "fields/thu/relative",
b331163b
A
551 "fields/thu-short/relative",
552 "fields/thu-narrow/relative",
57a6839d
A
553 daysOfWeek,
554 UDAT_ABSOLUTE_THURSDAY,
555 cacheData.absoluteUnits,
556 status);
b331163b 557 addWeekDays(
57a6839d
A
558 resource,
559 "fields/fri/relative",
b331163b
A
560 "fields/fri-short/relative",
561 "fields/fri-narrow/relative",
57a6839d
A
562 daysOfWeek,
563 UDAT_ABSOLUTE_FRIDAY,
564 cacheData.absoluteUnits,
565 status);
b331163b 566 addWeekDays(
57a6839d
A
567 resource,
568 "fields/sat/relative",
b331163b
A
569 "fields/sat-short/relative",
570 "fields/sat-narrow/relative",
57a6839d
A
571 daysOfWeek,
572 UDAT_ABSOLUTE_SATURDAY,
573 cacheData.absoluteUnits,
574 status);
b331163b 575 addWeekDays(
57a6839d
A
576 resource,
577 "fields/sun/relative",
b331163b
A
578 "fields/sun-short/relative",
579 "fields/sun-narrow/relative",
57a6839d
A
580 daysOfWeek,
581 UDAT_ABSOLUTE_SUNDAY,
582 cacheData.absoluteUnits,
583 status);
584 return U_SUCCESS(status);
585}
586
587static 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
b331163b
A
619template<> U_I18N_API
620const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
621 const char *localeId = fLoc.getName();
57a6839d
A
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 }
b331163b 647 result->addRef();
57a6839d
A
648 return result.orphan();
649}
650
b331163b
A
651RelativeDateTimeFormatter::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);
57a6839d
A
659}
660
b331163b
A
661RelativeDateTimeFormatter::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);
57a6839d
A
671}
672
673RelativeDateTimeFormatter::RelativeDateTimeFormatter(
b331163b
A
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);
57a6839d
A
683}
684
685RelativeDateTimeFormatter::RelativeDateTimeFormatter(
b331163b
A
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 }
57a6839d
A
714}
715
716RelativeDateTimeFormatter::RelativeDateTimeFormatter(
717 const RelativeDateTimeFormatter& other)
b331163b
A
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 }
57a6839d
A
732}
733
734RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
735 const RelativeDateTimeFormatter& other) {
736 if (this != &other) {
b331163b
A
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;
57a6839d
A
744 }
745 return *this;
746}
747
748RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
b331163b
A
749 if (fCache != NULL) {
750 fCache->removeRef();
751 }
752 if (fNumberFormat != NULL) {
753 fNumberFormat->removeRef();
57a6839d 754 }
b331163b
A
755 if (fPluralRules != NULL) {
756 fPluralRules->removeRef();
57a6839d 757 }
b331163b
A
758 if (fOptBreakIterator != NULL) {
759 fOptBreakIterator->removeRef();
57a6839d
A
760 }
761}
762
763const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
b331163b
A
764 return **fNumberFormat;
765}
766
767UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
768 return fContext;
769}
770
771UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
772 return fStyle;
57a6839d
A
773}
774
775UnicodeString& 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);
b331163b
A
787 if (fOptBreakIterator == NULL) {
788 return fCache->relativeUnits[fStyle][unit][bFuture].format(
57a6839d 789 quantity,
b331163b
A
790 **fNumberFormat,
791 **fPluralRules,
57a6839d
A
792 appendTo,
793 pos,
794 status);
b331163b
A
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);
57a6839d
A
806}
807
808UnicodeString& 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 }
b331163b
A
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);
57a6839d
A
824}
825
826UnicodeString& 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);
b331163b 831 return fCache->getCombinedDateAndTime()->format(
57a6839d
A
832 args, 2, appendTo, fpos, status);
833}
834
b331163b
A
835void 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
57a6839d 850void RelativeDateTimeFormatter::init(
b331163b
A
851 NumberFormat *nfToAdopt,
852 BreakIterator *biToAdopt,
853 UErrorCode &status) {
57a6839d 854 LocalPointer<NumberFormat> nf(nfToAdopt);
b331163b
A
855 LocalPointer<BreakIterator> bi(biToAdopt);
856 UnifiedCache::getByLocale(fLocale, fCache, status);
857 if (U_FAILURE(status)) {
57a6839d
A
858 return;
859 }
b331163b
A
860 const SharedPluralRules *pr = PluralRules::createSharedInstance(
861 fLocale, UPLURAL_TYPE_CARDINAL, status);
57a6839d
A
862 if (U_FAILURE(status)) {
863 return;
864 }
b331163b
A
865 SharedObject::copyPtr(pr, fPluralRules);
866 pr->removeRef();
57a6839d 867 if (nf.isNull()) {
b331163b
A
868 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
869 fLocale, UNUM_DECIMAL, status);
57a6839d
A
870 if (U_FAILURE(status)) {
871 return;
872 }
b331163b
A
873 SharedObject::copyPtr(shared, fNumberFormat);
874 shared->removeRef();
57a6839d
A
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();
b331163b
A
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);
57a6839d
A
894 }
895}
896
897
898U_NAMESPACE_END
899
900#endif /* !UCONFIG_NO_FORMATTING */
901