]>
Commit | Line | Data |
---|---|---|
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 | 37 | static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER; |
57a6839d A |
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. | |
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 | } | |
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.add( | |
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 | ||
b331163b | 321 | static 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 | ||
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( | |
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 | ||
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( | |
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 | ||
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 | ||
b331163b A |
619 | template<> U_I18N_API |
620 | const 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 |
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); | |
57a6839d A |
659 | } |
660 | ||
b331163b A |
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); | |
57a6839d A |
671 | } |
672 | ||
673 | RelativeDateTimeFormatter::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 | ||
685 | RelativeDateTimeFormatter::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 | ||
716 | RelativeDateTimeFormatter::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 | ||
734 | RelativeDateTimeFormatter& 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 | ||
748 | RelativeDateTimeFormatter::~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 | ||
763 | const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const { | |
b331163b A |
764 | return **fNumberFormat; |
765 | } | |
766 | ||
767 | UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const { | |
768 | return fContext; | |
769 | } | |
770 | ||
771 | UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const { | |
772 | return fStyle; | |
57a6839d A |
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); | |
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 | ||
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 | } | |
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 | ||
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); | |
b331163b | 831 | return fCache->getCombinedDateAndTime()->format( |
57a6839d A |
832 | args, 2, appendTo, fpos, status); |
833 | } | |
834 | ||
b331163b A |
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 | ||
57a6839d | 850 | void 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 | ||
898 | U_NAMESPACE_END | |
899 | ||
900 | #endif /* !UCONFIG_NO_FORMATTING */ | |
901 |