Imported Upstream version 58.1
[platform/upstream/icu.git] / source / i18n / reldatefmt.cpp
1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 * Copyright (C) 2014-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ******************************************************************************
8 *
9 * File reldatefmt.cpp
10 ******************************************************************************
11 */
12
13 #include "unicode/reldatefmt.h"
14
15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
16
17 #include "unicode/dtfmtsym.h"
18 #include "unicode/ureldatefmt.h"
19 #include "unicode/udisplaycontext.h"
20 #include "unicode/unum.h"
21 #include "unicode/localpointer.h"
22 #include "unicode/plurrule.h"
23 #include "unicode/simpleformatter.h"
24 #include "unicode/decimfmt.h"
25 #include "unicode/numfmt.h"
26 #include "unicode/brkiter.h"
27 #include "unicode/simpleformatter.h"
28 #include "uresimp.h"
29 #include "unicode/ures.h"
30 #include "cstring.h"
31 #include "ucln_in.h"
32 #include "mutex.h"
33 #include "charstr.h"
34 #include "uassert.h"
35 #include "quantityformatter.h"
36 #include "resource.h"
37 #include "sharedbreakiterator.h"
38 #include "sharedpluralrules.h"
39 #include "sharednumberformat.h"
40 #include "standardplural.h"
41 #include "unifiedcache.h"
42
43 // Copied from uscript_props.cpp
44
45 static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
46
47 U_NAMESPACE_BEGIN
48
49 // RelativeDateTimeFormatter specific data for a single locale
50 class RelativeDateTimeCacheData: public SharedObject {
51 public:
52     RelativeDateTimeCacheData() : combinedDateAndTime(NULL) {
53         // Initialize the cache arrays
54         for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
55             for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
56                 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
57                     relativeUnitsFormatters[style][relUnit][0][pl] = NULL;
58                     relativeUnitsFormatters[style][relUnit][1][pl] = NULL;
59                 }
60             }
61         }
62         for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
63           fallBackCache[i] = -1;
64         }
65     }
66     virtual ~RelativeDateTimeCacheData();
67
68     // no numbers: e.g Next Tuesday; Yesterday; etc.
69     UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
70
71     // SimpleFormatter pointers for relative unit format,
72     // e.g., Next Tuesday; Yesterday; etc. For third index, 0
73     // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
74     SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
75         [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT];
76
77     const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
78                                                UDateAbsoluteUnit unit,
79                                                UDateDirection direction) const;
80     const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
81                                                     UDateRelativeUnit unit,
82                                                     int32_t pastFutureIndex,
83                                                     int32_t pluralUnit) const;
84
85     const UnicodeString emptyString;
86
87     // Mappping from source to target styles for alias fallback.
88     int32_t fallBackCache[UDAT_STYLE_COUNT];
89
90     void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
91         delete combinedDateAndTime;
92         combinedDateAndTime = fmtToAdopt;
93     }
94     const SimpleFormatter *getCombinedDateAndTime() const {
95         return combinedDateAndTime;
96     }
97
98 private:
99     SimpleFormatter *combinedDateAndTime;
100     RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
101     RelativeDateTimeCacheData& operator=(
102             const RelativeDateTimeCacheData &other);
103 };
104
105 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
106     // clear out the cache arrays
107     for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
108         for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
109             for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
110                 delete relativeUnitsFormatters[style][relUnit][0][pl];
111                 delete relativeUnitsFormatters[style][relUnit][1][pl];
112             }
113         }
114     }
115     delete combinedDateAndTime;
116 }
117
118
119 // Use fallback cache for absolute units.
120 const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
121         int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
122     int32_t style = fStyle;
123     do {
124         if (!absoluteUnits[style][unit][direction].isEmpty()) {
125             return absoluteUnits[style][unit][direction];
126         }
127         style = fallBackCache[style];
128     } while (style != -1);
129     return emptyString;
130 }
131
132  // Use fallback cache for SimpleFormatter relativeUnits.
133  const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
134         int32_t fStyle,
135         UDateRelativeUnit unit,
136         int32_t pastFutureIndex,
137         int32_t pluralUnit) const {
138     int32_t style = fStyle;
139     do {
140         if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
141             return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
142         }
143         style = fallBackCache[style];
144     } while (style != -1);
145     return NULL;  // No formatter found.
146  }
147
148 static UBool getStringWithFallback(
149         const UResourceBundle *resource,
150         const char *key,
151         UnicodeString &result,
152         UErrorCode &status) {
153     int32_t len = 0;
154     const UChar *resStr = ures_getStringByKeyWithFallback(
155         resource, key, &len, &status);
156     if (U_FAILURE(status)) {
157         return FALSE;
158     }
159     result.setTo(TRUE, resStr, len);
160     return TRUE;
161 }
162
163
164 static UBool getStringByIndex(
165         const UResourceBundle *resource,
166         int32_t idx,
167         UnicodeString &result,
168         UErrorCode &status) {
169     int32_t len = 0;
170     const UChar *resStr = ures_getStringByIndex(
171             resource, idx, &len, &status);
172     if (U_FAILURE(status)) {
173         return FALSE;
174     }
175     result.setTo(TRUE, resStr, len);
176     return TRUE;
177 }
178
179 namespace {
180
181 /**
182  * Sink for enumerating all of the measurement unit display names.
183  *
184  * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
185  * Only store a value if it is still missing, that is, it has not been overridden.
186  */
187 struct RelDateTimeFmtDataSink : public ResourceSink {
188
189     /**
190      * Sink for patterns for relative dates and times. For example,
191      * fields/relative/...
192      */
193
194     // Generic unit enum for storing Unit info.
195     typedef enum RelAbsUnit {
196         INVALID_UNIT = -1,
197         SECOND,
198         MINUTE,
199         HOUR,
200         DAY,
201         WEEK,
202         MONTH,
203         QUARTER,
204         YEAR,
205         SUNDAY,
206         MONDAY,
207         TUESDAY,
208         WEDNESDAY,
209         THURSDAY,
210         FRIDAY,
211         SATURDAY
212     } RelAbsUnit;
213
214     static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
215         // Converts the generic units to UDAT_RELATIVE version.
216         switch (genUnit) {
217             case SECOND:
218                 return UDAT_RELATIVE_SECONDS;
219             case MINUTE:
220                 return UDAT_RELATIVE_MINUTES;
221             case HOUR:
222                 return UDAT_RELATIVE_HOURS;
223             case DAY:
224                 return UDAT_RELATIVE_DAYS;
225             case WEEK:
226                 return UDAT_RELATIVE_WEEKS;
227             case MONTH:
228                 return UDAT_RELATIVE_MONTHS;
229             /*
230              * case QUARTER:
231              * return UDATE_RELATIVE_QUARTERS;
232              */
233             case YEAR:
234                 return UDAT_RELATIVE_YEARS;
235             default:
236                 return -1;
237         }
238     }
239
240     static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
241         // Converts the generic units to UDAT_RELATIVE version.
242         switch (genUnit) {
243             case DAY:
244                 return UDAT_ABSOLUTE_DAY;
245             case WEEK:
246                 return UDAT_ABSOLUTE_WEEK;
247             case MONTH:
248                 return UDAT_ABSOLUTE_MONTH;
249             /* TODO: Add in QUARTER
250              *  case QUARTER:
251              * return UDAT_ABSOLUTE_QUARTER;
252              */
253             case YEAR:
254                 return UDAT_ABSOLUTE_YEAR;
255             case SUNDAY:
256                 return UDAT_ABSOLUTE_SUNDAY;
257             case MONDAY:
258                 return UDAT_ABSOLUTE_MONDAY;
259             case TUESDAY:
260                 return UDAT_ABSOLUTE_TUESDAY;
261             case WEDNESDAY:
262                 return UDAT_ABSOLUTE_WEDNESDAY;
263             case THURSDAY:
264                 return UDAT_ABSOLUTE_THURSDAY;
265             case FRIDAY:
266                 return UDAT_ABSOLUTE_FRIDAY;
267             case SATURDAY:
268                 return UDAT_ABSOLUTE_SATURDAY;
269             default:
270                 return -1;
271         }
272     }
273
274     static int32_t keyToDirection(const char* key) {
275         if (uprv_strcmp(key, "-2") == 0) {
276             return UDAT_DIRECTION_LAST_2;
277         }
278         if (uprv_strcmp(key, "-1") == 0) {
279             return UDAT_DIRECTION_LAST;
280         }
281         if (uprv_strcmp(key, "0") == 0) {
282             return UDAT_DIRECTION_THIS;
283         }
284         if (uprv_strcmp(key, "1") == 0) {
285             return UDAT_DIRECTION_NEXT;
286         }
287         if (uprv_strcmp(key, "2") == 0) {
288             return UDAT_DIRECTION_NEXT_2;
289         }
290         return -1;
291     }
292
293     // Values kept between levels of parsing the CLDR data.
294     int32_t pastFutureIndex;  // 0 == past or 1 ==  future
295     UDateRelativeDateTimeFormatterStyle style;  // {LONG, SHORT, NARROW}
296     RelAbsUnit genericUnit;
297
298     RelativeDateTimeCacheData &outputData;
299
300     // Constructor
301     RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
302         : outputData(cacheData) {
303         // Clear cacheData.fallBackCache
304         cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
305         cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
306         cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
307     }
308
309     ~RelDateTimeFmtDataSink();
310
311     // Utility functions
312     static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
313         int32_t len = uprv_strlen(s);
314         if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
315             return UDAT_STYLE_NARROW;
316         }
317         if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
318             return UDAT_STYLE_SHORT;
319         }
320         return UDAT_STYLE_LONG;
321     }
322
323     static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
324         switch (style) {
325             case UDAT_STYLE_NARROW:
326                 return 7;
327             case UDAT_STYLE_SHORT:
328                 return 6;
329             default:
330                 return 0;
331         }
332     }
333
334     // Utility functions
335     static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
336         static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
337         static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
338         if (s.endsWith(narrow, 7)) {
339             return UDAT_STYLE_NARROW;
340         }
341         if (s.endsWith(sshort, 6)) {
342             return UDAT_STYLE_SHORT;
343         }
344         return UDAT_STYLE_LONG;
345     }
346
347     static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
348         // Quick check from string to enum.
349         switch (length) {
350             case 3:
351                 if (uprv_strncmp(keyword, "day", length) == 0) {
352                     return DAY;
353                 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
354                     return SUNDAY;
355                 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
356                     return MONDAY;
357                 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
358                     return TUESDAY;
359                 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
360                     return WEDNESDAY;
361                 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
362                     return THURSDAY;
363                 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
364                     return FRIDAY;
365                 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
366                     return SATURDAY;
367                 }
368                 break;
369             case 4:
370                 if (uprv_strncmp(keyword, "hour", length) == 0) {
371                     return HOUR;
372                 } else if (uprv_strncmp(keyword, "week", length) == 0) {
373                     return WEEK;
374                 } else if (uprv_strncmp(keyword, "year", length) == 0) {
375                     return YEAR;
376                 }
377                 break;
378             case 5:
379                 if (uprv_strncmp(keyword, "month", length) == 0) {
380                     return MONTH;
381                 }
382                 break;
383             case 6:
384                 if (uprv_strncmp(keyword, "minute", length) == 0) {
385                     return MINUTE;
386                 } else if (uprv_strncmp(keyword, "second", length) == 0) {
387                     return SECOND;
388                 }
389                 break;
390             case 7:
391                 if (uprv_strncmp(keyword, "quarter", length) == 0) {
392                     return QUARTER;  // TODO: Check @provisional
393                   }
394                 break;
395             default:
396                 break;
397         }
398         return INVALID_UNIT;
399     }
400
401     void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
402         // Handle Display Name for PLAIN direction for some units.
403         if (U_FAILURE(errorCode)) { return; }
404
405         int32_t absUnit = absUnitFromGeneric(genericUnit);
406         if (absUnit < 0) {
407           return;  // Not interesting.
408         }
409
410         // Store displayname if not set.
411         if (outputData.absoluteUnits[style]
412             [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
413             outputData.absoluteUnits[style]
414                 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
415             return;
416         }
417     }
418
419     void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
420         ResourceTable unitTypesTable = value.getTable(errorCode);
421         if (U_FAILURE(errorCode)) { return; }
422
423         for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
424             if (value.getType() == URES_STRING) {
425                 int32_t direction = keyToDirection(key);
426                 if (direction < 0) {
427                   continue;
428                 }
429
430                 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
431                 if (relUnitIndex == UDAT_RELATIVE_SECONDS && uprv_strcmp(key, "0") == 0 &&
432                     outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
433                     // Handle "NOW"
434                     outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
435                         [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
436                 }
437
438                 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
439                 if (absUnitIndex < 0) {
440                     continue;
441                 }
442                 // Only reset if slot is empty.
443                 if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
444                     outputData.absoluteUnits[style][absUnitIndex]
445                         [direction].fastCopyFrom(value.getUnicodeString(errorCode));
446                 }
447             }
448         }
449     }
450
451     void consumeTimeDetail(int32_t relUnitIndex,
452                            const char *key, ResourceValue &value, UErrorCode &errorCode) {
453         ResourceTable unitTypesTable = value.getTable(errorCode);
454         if (U_FAILURE(errorCode)) { return; }
455
456           for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
457             if (value.getType() == URES_STRING) {
458                 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
459                 if (pluralIndex >= 0) {
460                     SimpleFormatter **patterns =
461                         outputData.relativeUnitsFormatters[style][relUnitIndex]
462                         [pastFutureIndex];
463                     // Only set if not already established.
464                     if (patterns[pluralIndex] == NULL) {
465                         patterns[pluralIndex] = new SimpleFormatter(
466                             value.getUnicodeString(errorCode), 0, 1, errorCode);
467                         if (patterns[pluralIndex] == NULL) {
468                             errorCode = U_MEMORY_ALLOCATION_ERROR;
469                         }
470                     }
471                 }
472             }
473         }
474     }
475
476     void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
477         ResourceTable relativeTimeTable = value.getTable(errorCode);
478         if (U_FAILURE(errorCode)) { return; }
479
480         int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
481         if (relUnitIndex < 0) {
482             return;
483         }
484         for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
485             if (uprv_strcmp(key, "past") == 0) {
486                 pastFutureIndex = 0;
487             } else if (uprv_strcmp(key, "future") == 0) {
488                 pastFutureIndex = 1;
489             } else {
490                 // Unknown key.
491                 continue;
492             }
493             consumeTimeDetail(relUnitIndex, key, value, errorCode);
494         }
495     }
496
497     void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
498
499         UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
500         const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
501         if (U_FAILURE(errorCode)) { return; }
502
503         UDateRelativeDateTimeFormatterStyle targetStyle =
504             styleFromAliasUnicodeString(valueStr);
505
506         if (sourceStyle == targetStyle) {
507             errorCode = U_INVALID_FORMAT_ERROR;
508             return;
509         }
510         if (outputData.fallBackCache[sourceStyle] != -1 &&
511             outputData.fallBackCache[sourceStyle] != targetStyle) {
512             errorCode = U_INVALID_FORMAT_ERROR;
513             return;
514         }
515         outputData.fallBackCache[sourceStyle] = targetStyle;
516     }
517
518     void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
519         ResourceTable unitTypesTable = value.getTable(errorCode);
520         if (U_FAILURE(errorCode)) { return; }
521
522         for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
523             // Handle display name.
524             if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
525                 handlePlainDirection(value, errorCode);
526             }
527             if (value.getType() == URES_TABLE) {
528                 if (uprv_strcmp(key, "relative") == 0) {
529                     consumeTableRelative(key, value, errorCode);
530                 } else if (uprv_strcmp(key, "relativeTime") == 0) {
531                     consumeTableRelativeTime(key, value, errorCode);
532                 }
533             }
534         }
535     }
536
537     virtual void put(const char *key, ResourceValue &value,
538                      UBool /*noFallback*/, UErrorCode &errorCode) {
539         // Main entry point to sink
540         ResourceTable table = value.getTable(errorCode);
541         if (U_FAILURE(errorCode)) { return; }
542         for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
543             if (value.getType() == URES_ALIAS) {
544                 consumeAlias(key, value, errorCode);
545             } else {
546                 style = styleFromString(key);
547                 int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
548                 genericUnit = unitOrNegativeFromString(key, unitSize);
549                 if (style >= 0 && genericUnit != INVALID_UNIT) {
550                     consumeTimeUnit(key, value, errorCode);
551                 }
552             }
553         }
554     }
555
556 };
557
558 // Virtual destructors must be defined out of line.
559 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
560 } // namespace
561
562 DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
563   DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
564 };
565
566 // Get days of weeks from the DateFormatSymbols class.
567 static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
568                                  [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
569                              const char* localeId,
570                              UErrorCode& status) {
571     Locale locale(localeId);
572     DateFormatSymbols dfSym(locale, status);
573     for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
574         DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
575         int32_t count;
576         const UnicodeString* weekdayNames =
577             dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
578         for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
579                 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
580             int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
581             absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
582                 weekdayNames[dateSymbolIndex]);
583         }
584     }
585 }
586
587 static UBool loadUnitData(
588         const UResourceBundle *resource,
589         RelativeDateTimeCacheData &cacheData,
590         const char* localeId,
591         UErrorCode &status) {
592
593     RelDateTimeFmtDataSink sink(cacheData);
594
595     ures_getAllItemsWithFallback(resource, "fields", sink, status);
596
597     // Get the weekday names from DateFormatSymbols.
598     loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
599     return U_SUCCESS(status);
600 }
601
602 static UBool getDateTimePattern(
603         const UResourceBundle *resource,
604         UnicodeString &result,
605         UErrorCode &status) {
606     UnicodeString defaultCalendarName;
607     if (!getStringWithFallback(
608             resource,
609             "calendar/default",
610             defaultCalendarName,
611             status)) {
612         return FALSE;
613     }
614     CharString pathBuffer;
615     pathBuffer.append("calendar/", status)
616             .appendInvariantChars(defaultCalendarName, status)
617             .append("/DateTimePatterns", status);
618     LocalUResourceBundlePointer topLevel(
619             ures_getByKeyWithFallback(
620                     resource, pathBuffer.data(), NULL, &status));
621     if (U_FAILURE(status)) {
622         return FALSE;
623     }
624     int32_t size = ures_getSize(topLevel.getAlias());
625     if (size <= 8) {
626         // Oops, size is too small to access the index that we want, fallback
627         // to a hard-coded value.
628         result = UNICODE_STRING_SIMPLE("{1} {0}");
629         return TRUE;
630     }
631     return getStringByIndex(topLevel.getAlias(), 8, result, status);
632 }
633
634 template<> U_I18N_API
635 const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
636     const char *localeId = fLoc.getName();
637     LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
638     if (U_FAILURE(status)) {
639         return NULL;
640     }
641     LocalPointer<RelativeDateTimeCacheData> result(
642             new RelativeDateTimeCacheData());
643     if (result.isNull()) {
644         status = U_MEMORY_ALLOCATION_ERROR;
645         return NULL;
646     }
647     if (!loadUnitData(
648             topLevel.getAlias(),
649             *result,
650             localeId,
651             status)) {
652         return NULL;
653     }
654     UnicodeString dateTimePattern;
655     if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
656         return NULL;
657     }
658     result->adoptCombinedDateAndTime(
659             new SimpleFormatter(dateTimePattern, 2, 2, status));
660     if (U_FAILURE(status)) {
661         return NULL;
662     }
663     result->addRef();
664     return result.orphan();
665 }
666
667 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
668         fCache(NULL),
669         fNumberFormat(NULL),
670         fPluralRules(NULL),
671         fStyle(UDAT_STYLE_LONG),
672         fContext(UDISPCTX_CAPITALIZATION_NONE),
673         fOptBreakIterator(NULL) {
674     init(NULL, NULL, status);
675 }
676
677 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
678         const Locale& locale, UErrorCode& status) :
679         fCache(NULL),
680         fNumberFormat(NULL),
681         fPluralRules(NULL),
682         fStyle(UDAT_STYLE_LONG),
683         fContext(UDISPCTX_CAPITALIZATION_NONE),
684         fOptBreakIterator(NULL),
685         fLocale(locale) {
686     init(NULL, NULL, status);
687 }
688
689 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
690         const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
691         fCache(NULL),
692         fNumberFormat(NULL),
693         fPluralRules(NULL),
694         fStyle(UDAT_STYLE_LONG),
695         fContext(UDISPCTX_CAPITALIZATION_NONE),
696         fOptBreakIterator(NULL),
697         fLocale(locale) {
698     init(nfToAdopt, NULL, status);
699 }
700
701 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
702         const Locale& locale,
703         NumberFormat *nfToAdopt,
704         UDateRelativeDateTimeFormatterStyle styl,
705         UDisplayContext capitalizationContext,
706         UErrorCode& status) :
707         fCache(NULL),
708         fNumberFormat(NULL),
709         fPluralRules(NULL),
710         fStyle(styl),
711         fContext(capitalizationContext),
712         fOptBreakIterator(NULL),
713         fLocale(locale) {
714     if (U_FAILURE(status)) {
715         return;
716     }
717     if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
718         status = U_ILLEGAL_ARGUMENT_ERROR;
719         return;
720     }
721     if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
722         BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
723         if (U_FAILURE(status)) {
724             return;
725         }
726         init(nfToAdopt, bi, status);
727     } else {
728         init(nfToAdopt, NULL, status);
729     }
730 }
731
732 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
733         const RelativeDateTimeFormatter& other)
734         : UObject(other),
735           fCache(other.fCache),
736           fNumberFormat(other.fNumberFormat),
737           fPluralRules(other.fPluralRules),
738           fStyle(other.fStyle),
739           fContext(other.fContext),
740           fOptBreakIterator(other.fOptBreakIterator),
741           fLocale(other.fLocale) {
742     fCache->addRef();
743     fNumberFormat->addRef();
744     fPluralRules->addRef();
745     if (fOptBreakIterator != NULL) {
746       fOptBreakIterator->addRef();
747     }
748 }
749
750 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
751         const RelativeDateTimeFormatter& other) {
752     if (this != &other) {
753         SharedObject::copyPtr(other.fCache, fCache);
754         SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
755         SharedObject::copyPtr(other.fPluralRules, fPluralRules);
756         SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
757         fStyle = other.fStyle;
758         fContext = other.fContext;
759         fLocale = other.fLocale;
760     }
761     return *this;
762 }
763
764 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
765     if (fCache != NULL) {
766         fCache->removeRef();
767     }
768     if (fNumberFormat != NULL) {
769         fNumberFormat->removeRef();
770     }
771     if (fPluralRules != NULL) {
772         fPluralRules->removeRef();
773     }
774     if (fOptBreakIterator != NULL) {
775         fOptBreakIterator->removeRef();
776     }
777 }
778
779 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
780     return **fNumberFormat;
781 }
782
783 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
784     return fContext;
785 }
786
787 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
788     return fStyle;
789 }
790
791 UnicodeString& RelativeDateTimeFormatter::format(
792         double quantity, UDateDirection direction, UDateRelativeUnit unit,
793         UnicodeString& appendTo, UErrorCode& status) const {
794     if (U_FAILURE(status)) {
795         return appendTo;
796     }
797     if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
798         status = U_ILLEGAL_ARGUMENT_ERROR;
799         return appendTo;
800     }
801     int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
802     FieldPosition pos(FieldPosition::DONT_CARE);
803
804     UnicodeString result;
805     UnicodeString formattedNumber;
806
807     StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
808         quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
809         status);
810
811     const SimpleFormatter* formatter =
812         fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
813     if (formatter == NULL) {
814         // TODO: WARN - look at quantity formatter's action with an error.
815         status = U_INVALID_FORMAT_ERROR;
816         return appendTo;
817     }
818     formatter->format(formattedNumber, result, status);
819     adjustForContext(result);
820     return appendTo.append(result);
821 }
822
823 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
824         double offset, URelativeDateTimeUnit unit,
825         UnicodeString& appendTo, UErrorCode& status) const {
826     if (U_FAILURE(status)) {
827         return appendTo;
828     }
829     // TODO:
830     // The full implementation of this depends on CLDR data that is not yet available,
831     // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
832     // In the meantime do a quick bring-up by calling the old format method; this
833     // leaves some holes (even for data that is currently available, such as quarter).
834     // When the new CLDR data is available, update the data storage accordingly,
835     // rewrite this to use it directly, and rewrite the old format method to call this
836     // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
837     UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT;
838     switch (unit) {
839         case UDAT_REL_UNIT_YEAR:    relunit = UDAT_RELATIVE_YEARS; break;
840         case UDAT_REL_UNIT_MONTH:   relunit = UDAT_RELATIVE_MONTHS; break;
841         case UDAT_REL_UNIT_WEEK:    relunit = UDAT_RELATIVE_WEEKS; break;
842         case UDAT_REL_UNIT_DAY:     relunit = UDAT_RELATIVE_DAYS; break;
843         case UDAT_REL_UNIT_HOUR:    relunit = UDAT_RELATIVE_HOURS; break;
844         case UDAT_REL_UNIT_MINUTE:  relunit = UDAT_RELATIVE_MINUTES; break;
845         case UDAT_REL_UNIT_SECOND:  relunit = UDAT_RELATIVE_SECONDS; break;
846         default: // a unit that the above method does not handle
847             status = U_UNSUPPORTED_ERROR;
848             return appendTo;
849     }
850     UDateDirection direction = UDAT_DIRECTION_NEXT;
851     if (offset < 0) {
852         direction = UDAT_DIRECTION_LAST;
853         offset = -offset;
854     }
855     return format(offset, direction, relunit, appendTo, status);
856 }
857
858 UnicodeString& RelativeDateTimeFormatter::format(
859         UDateDirection direction, UDateAbsoluteUnit unit,
860         UnicodeString& appendTo, UErrorCode& status) const {
861     if (U_FAILURE(status)) {
862         return appendTo;
863     }
864     if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
865         status = U_ILLEGAL_ARGUMENT_ERROR;
866         return appendTo;
867     }
868
869     // Get string using fallback.
870     UnicodeString result;
871     result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
872     if (fOptBreakIterator != NULL) {
873         adjustForContext(result);
874     }
875     return appendTo.append(result);
876 }
877
878 UnicodeString& RelativeDateTimeFormatter::format(
879         double offset, URelativeDateTimeUnit unit,
880         UnicodeString& appendTo, UErrorCode& status) const {
881     if (U_FAILURE(status)) {
882         return appendTo;
883     }
884     // TODO:
885     // The full implementation of this depends on CLDR data that is not yet available,
886     // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
887     // In the meantime do a quick bring-up by calling the old format method; this
888     // leaves some holes (even for data that is currently available, such as quarter).
889     // When the new CLDR data is available, update the data storage accordingly,
890     // rewrite this to use it directly, and rewrite the old format method to call this
891     // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
892     UDateDirection direction = UDAT_DIRECTION_COUNT;
893     if (offset > -2.1 && offset < 2.1) {
894         // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
895         double offsetx100 = offset * 100.0;
896         int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
897         switch (intoffset) {
898             case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
899             case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
900             case    0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
901             case  100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
902             case  200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
903             default: break;
904         }
905     }
906     UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
907     switch (unit) {
908         case UDAT_REL_UNIT_YEAR:    absunit = UDAT_ABSOLUTE_YEAR; break;
909         case UDAT_REL_UNIT_MONTH:   absunit = UDAT_ABSOLUTE_MONTH; break;
910         case UDAT_REL_UNIT_WEEK:    absunit = UDAT_ABSOLUTE_WEEK; break;
911         case UDAT_REL_UNIT_DAY:     absunit = UDAT_ABSOLUTE_DAY; break;
912         case UDAT_REL_UNIT_SECOND:
913             if (direction == UDAT_DIRECTION_THIS) {
914                 absunit = UDAT_ABSOLUTE_NOW;
915                 direction = UDAT_DIRECTION_PLAIN;
916             }
917             break;
918         case UDAT_REL_UNIT_SUNDAY:  absunit = UDAT_ABSOLUTE_SUNDAY; break;
919         case UDAT_REL_UNIT_MONDAY:  absunit = UDAT_ABSOLUTE_MONDAY; break;
920         case UDAT_REL_UNIT_TUESDAY:  absunit = UDAT_ABSOLUTE_TUESDAY; break;
921         case UDAT_REL_UNIT_WEDNESDAY:  absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
922         case UDAT_REL_UNIT_THURSDAY:  absunit = UDAT_ABSOLUTE_THURSDAY; break;
923         case UDAT_REL_UNIT_FRIDAY:  absunit = UDAT_ABSOLUTE_FRIDAY; break;
924         case UDAT_REL_UNIT_SATURDAY:  absunit = UDAT_ABSOLUTE_SATURDAY; break;
925         default: break;
926     }
927     if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
928         const UnicodeString &unitFormatString =
929             fCache->getAbsoluteUnitString(fStyle, absunit, direction);
930         if (!unitFormatString.isEmpty()) {
931             if (fOptBreakIterator != NULL) {
932                 UnicodeString result(unitFormatString);
933                 adjustForContext(result);
934                 return appendTo.append(result);
935             } else {
936                 return appendTo.append(unitFormatString);
937             }
938         }
939     }
940     // otherwise fallback to formatNumeric
941     return formatNumeric(offset, unit, appendTo, status);
942 }
943
944 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
945         const UnicodeString& relativeDateString, const UnicodeString& timeString,
946         UnicodeString& appendTo, UErrorCode& status) const {
947     return fCache->getCombinedDateAndTime()->format(
948             timeString, relativeDateString, appendTo, status);
949 }
950
951 void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
952     if (fOptBreakIterator == NULL
953         || str.length() == 0 || !u_islower(str.char32At(0))) {
954         return;
955     }
956
957     // Must guarantee that one thread at a time accesses the shared break
958     // iterator.
959     Mutex lock(&gBrkIterMutex);
960     str.toTitle(
961             fOptBreakIterator->get(),
962             fLocale,
963             U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
964 }
965
966 void RelativeDateTimeFormatter::init(
967         NumberFormat *nfToAdopt,
968         BreakIterator *biToAdopt,
969         UErrorCode &status) {
970     LocalPointer<NumberFormat> nf(nfToAdopt);
971     LocalPointer<BreakIterator> bi(biToAdopt);
972     UnifiedCache::getByLocale(fLocale, fCache, status);
973     if (U_FAILURE(status)) {
974         return;
975     }
976     const SharedPluralRules *pr = PluralRules::createSharedInstance(
977             fLocale, UPLURAL_TYPE_CARDINAL, status);
978     if (U_FAILURE(status)) {
979         return;
980     }
981     SharedObject::copyPtr(pr, fPluralRules);
982     pr->removeRef();
983     if (nf.isNull()) {
984        const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
985                fLocale, UNUM_DECIMAL, status);
986         if (U_FAILURE(status)) {
987             return;
988         }
989         SharedObject::copyPtr(shared, fNumberFormat);
990         shared->removeRef();
991     } else {
992         SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
993         if (shared == NULL) {
994             status = U_MEMORY_ALLOCATION_ERROR;
995             return;
996         }
997         nf.orphan();
998         SharedObject::copyPtr(shared, fNumberFormat);
999     }
1000     if (bi.isNull()) {
1001         SharedObject::clearPtr(fOptBreakIterator);
1002     } else {
1003         SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1004         if (shared == NULL) {
1005             status = U_MEMORY_ALLOCATION_ERROR;
1006             return;
1007         }
1008         bi.orphan();
1009         SharedObject::copyPtr(shared, fOptBreakIterator);
1010     }
1011 }
1012
1013 U_NAMESPACE_END
1014
1015 // Plain C API
1016
1017 U_NAMESPACE_USE
1018
1019 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1020 ureldatefmt_open( const char*          locale,
1021                   UNumberFormat*       nfToAdopt,
1022                   UDateRelativeDateTimeFormatterStyle width,
1023                   UDisplayContext      capitalizationContext,
1024                   UErrorCode*          status )
1025 {
1026     if (U_FAILURE(*status)) {
1027         return NULL;
1028     }
1029     LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1030                                                               (NumberFormat*)nfToAdopt, width,
1031                                                               capitalizationContext, *status), *status);
1032     if (U_FAILURE(*status)) {
1033         return NULL;
1034     }
1035     return (URelativeDateTimeFormatter*)formatter.orphan();
1036 }
1037
1038 U_CAPI void U_EXPORT2
1039 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1040 {
1041     delete (RelativeDateTimeFormatter*)reldatefmt;
1042 }
1043
1044 U_CAPI int32_t U_EXPORT2
1045 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1046                     double                offset,
1047                     URelativeDateTimeUnit unit,
1048                     UChar*                result,
1049                     int32_t               resultCapacity,
1050                     UErrorCode*           status)
1051 {
1052     if (U_FAILURE(*status)) {
1053         return 0;
1054     }
1055     if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1056         *status = U_ILLEGAL_ARGUMENT_ERROR;
1057         return 0;
1058     }
1059     UnicodeString res;
1060     if (result != NULL) {
1061         // NULL destination for pure preflighting: empty dummy string
1062         // otherwise, alias the destination buffer (copied from udat_format)
1063         res.setTo(result, 0, resultCapacity);
1064     }
1065     ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1066     if (U_FAILURE(*status)) {
1067         return 0;
1068     }
1069     return res.extract(result, resultCapacity, *status);
1070 }
1071
1072 U_CAPI int32_t U_EXPORT2
1073 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1074                     double                offset,
1075                     URelativeDateTimeUnit unit,
1076                     UChar*                result,
1077                     int32_t               resultCapacity,
1078                     UErrorCode*           status)
1079 {
1080     if (U_FAILURE(*status)) {
1081         return 0;
1082     }
1083     if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1084         *status = U_ILLEGAL_ARGUMENT_ERROR;
1085         return 0;
1086     }
1087     UnicodeString res;
1088     if (result != NULL) {
1089         // NULL destination for pure preflighting: empty dummy string
1090         // otherwise, alias the destination buffer (copied from udat_format)
1091         res.setTo(result, 0, resultCapacity);
1092     }
1093     ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1094     if (U_FAILURE(*status)) {
1095         return 0;
1096     }
1097     return res.extract(result, resultCapacity, *status);
1098 }
1099
1100 U_CAPI int32_t U_EXPORT2
1101 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1102                     const UChar *     relativeDateString,
1103                     int32_t           relativeDateStringLen,
1104                     const UChar *     timeString,
1105                     int32_t           timeStringLen,
1106                     UChar*            result,
1107                     int32_t           resultCapacity,
1108                     UErrorCode*       status )
1109 {
1110     if (U_FAILURE(*status)) {
1111         return 0;
1112     }
1113     if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 ||
1114             (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1115             (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) {
1116         *status = U_ILLEGAL_ARGUMENT_ERROR;
1117         return 0;
1118     }
1119     UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1120     UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1121     UnicodeString res(result, 0, resultCapacity);
1122     ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1123     if (U_FAILURE(*status)) {
1124         return 0;
1125     }
1126     return res.extract(result, resultCapacity, *status);
1127 }
1128
1129 #endif /* !UCONFIG_NO_FORMATTING */