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) 2008-2015, Google, International Business Machines Corporation
6 * and others. All Rights Reserved.
7 *******************************************************************************
8 */
9
10 #include "unicode/tmutfmt.h"
11
12 #if !UCONFIG_NO_FORMATTING
13
14 #include "unicode/decimfmt.h"
15 #include "unicode/localpointer.h"
16 #include "plurrule_impl.h"
17 #include "uvector.h"
18 #include "charstr.h"
19 #include "cmemory.h"
20 #include "cstring.h"
21 #include "hash.h"
22 #include "uresimp.h"
23 #include "ureslocs.h"
24 #include "unicode/msgfmt.h"
25 #include "uassert.h"
26
27 #define LEFT_CURLY_BRACKET ((UChar)0x007B)
28 #define RIGHT_CURLY_BRACKET ((UChar)0x007D)
29 #define SPACE ((UChar)0x0020)
30 #define DIGIT_ZERO ((UChar)0x0030)
31 #define LOW_S ((UChar)0x0073)
32 #define LOW_M ((UChar)0x006D)
33 #define LOW_I ((UChar)0x0069)
34 #define LOW_N ((UChar)0x006E)
35 #define LOW_H ((UChar)0x0068)
36 #define LOW_W ((UChar)0x0077)
37 #define LOW_D ((UChar)0x0064)
38 #define LOW_Y ((UChar)0x0079)
39 #define LOW_Z ((UChar)0x007A)
40 #define LOW_E ((UChar)0x0065)
41 #define LOW_R ((UChar)0x0072)
42 #define LOW_O ((UChar)0x006F)
43 #define LOW_N ((UChar)0x006E)
44 #define LOW_T ((UChar)0x0074)
45
46
47 //TODO: define in compile time
48 //#define TMUTFMT_DEBUG 1
49
50 #ifdef TMUTFMT_DEBUG
51 #include <iostream>
52 #endif
53
54 U_NAMESPACE_BEGIN
55
56
57
58 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
59
60 static const char gUnitsTag[] = "units";
61 static const char gShortUnitsTag[] = "unitsShort";
62 static const char gTimeUnitYear[] = "year";
63 static const char gTimeUnitMonth[] = "month";
64 static const char gTimeUnitDay[] = "day";
65 static const char gTimeUnitWeek[] = "week";
66 static const char gTimeUnitHour[] = "hour";
67 static const char gTimeUnitMinute[] = "minute";
68 static const char gTimeUnitSecond[] = "second";
69 static const char gPluralCountOther[] = "other";
70
71 static const UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
72 static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
73 static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
74 static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
75 static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
76 static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
77 static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
78
79 static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
80 static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
81 static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
82
TimeUnitFormat(UErrorCode & status)83 TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
84 initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status);
85 create(UTMUTFMT_FULL_STYLE, status);
86 }
87
88
TimeUnitFormat(const Locale & locale,UErrorCode & status)89 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
90 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
91 create(UTMUTFMT_FULL_STYLE, status);
92 }
93
94
TimeUnitFormat(const Locale & locale,UTimeUnitFormatStyle style,UErrorCode & status)95 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
96 switch (style) {
97 case UTMUTFMT_FULL_STYLE:
98 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
99 break;
100 case UTMUTFMT_ABBREVIATED_STYLE:
101 initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
102 break;
103 default:
104 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
105 break;
106 }
107 create(style, status);
108 }
109
TimeUnitFormat(const TimeUnitFormat & other)110 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
111 : MeasureFormat(other),
112 fStyle(other.fStyle)
113 {
114 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
115 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
116 i = (TimeUnit::UTimeUnitFields)(i+1)) {
117 UErrorCode status = U_ZERO_ERROR;
118 fTimeUnitToCountToPatterns[i] = initHash(status);
119 if (U_SUCCESS(status)) {
120 copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
121 } else {
122 delete fTimeUnitToCountToPatterns[i];
123 fTimeUnitToCountToPatterns[i] = NULL;
124 }
125 }
126 }
127
128
~TimeUnitFormat()129 TimeUnitFormat::~TimeUnitFormat() {
130 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
131 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
132 i = (TimeUnit::UTimeUnitFields)(i+1)) {
133 deleteHash(fTimeUnitToCountToPatterns[i]);
134 fTimeUnitToCountToPatterns[i] = NULL;
135 }
136 }
137
138
139 Format*
clone(void) const140 TimeUnitFormat::clone(void) const {
141 return new TimeUnitFormat(*this);
142 }
143
144
145 TimeUnitFormat&
operator =(const TimeUnitFormat & other)146 TimeUnitFormat::operator=(const TimeUnitFormat& other) {
147 if (this == &other) {
148 return *this;
149 }
150 MeasureFormat::operator=(other);
151 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
152 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
153 i = (TimeUnit::UTimeUnitFields)(i+1)) {
154 deleteHash(fTimeUnitToCountToPatterns[i]);
155 fTimeUnitToCountToPatterns[i] = NULL;
156 }
157 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
158 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
159 i = (TimeUnit::UTimeUnitFields)(i+1)) {
160 UErrorCode status = U_ZERO_ERROR;
161 fTimeUnitToCountToPatterns[i] = initHash(status);
162 if (U_SUCCESS(status)) {
163 copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
164 } else {
165 delete fTimeUnitToCountToPatterns[i];
166 fTimeUnitToCountToPatterns[i] = NULL;
167 }
168 }
169 fStyle = other.fStyle;
170 return *this;
171 }
172
173 void
parseObject(const UnicodeString & source,Formattable & result,ParsePosition & pos) const174 TimeUnitFormat::parseObject(const UnicodeString& source,
175 Formattable& result,
176 ParsePosition& pos) const {
177 Formattable resultNumber(0.0);
178 UBool withNumberFormat = false;
179 TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
180 int32_t oldPos = pos.getIndex();
181 int32_t newPos = -1;
182 int32_t longestParseDistance = 0;
183 UnicodeString* countOfLongestMatch = NULL;
184 #ifdef TMUTFMT_DEBUG
185 char res[1000];
186 source.extract(0, source.length(), res, "UTF-8");
187 std::cout << "parse source: " << res << "\n";
188 #endif
189 // parse by iterating through all available patterns
190 // and looking for the longest match.
191 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
192 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
193 i = (TimeUnit::UTimeUnitFields)(i+1)) {
194 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
195 int32_t elemPos = UHASH_FIRST;
196 const UHashElement* elem = NULL;
197 while ((elem = countToPatterns->nextElement(elemPos)) != NULL){
198 const UHashTok keyTok = elem->key;
199 UnicodeString* count = (UnicodeString*)keyTok.pointer;
200 #ifdef TMUTFMT_DEBUG
201 count->extract(0, count->length(), res, "UTF-8");
202 std::cout << "parse plural count: " << res << "\n";
203 #endif
204 const UHashTok valueTok = elem->value;
205 // the value is a pair of MessageFormat*
206 MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
207 for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
208 style = (UTimeUnitFormatStyle)(style + 1)) {
209 MessageFormat* pattern = patterns[style];
210 pos.setErrorIndex(-1);
211 pos.setIndex(oldPos);
212 // see if we can parse
213 Formattable parsed;
214 pattern->parseObject(source, parsed, pos);
215 if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
216 continue;
217 }
218 #ifdef TMUTFMT_DEBUG
219 std::cout << "parsed.getType: " << parsed.getType() << "\n";
220 #endif
221 Formattable tmpNumber(0.0);
222 if (pattern->getArgTypeCount() != 0) {
223 Formattable& temp = parsed[0];
224 if (temp.getType() == Formattable::kString) {
225 UnicodeString tmpString;
226 UErrorCode pStatus = U_ZERO_ERROR;
227 getNumberFormat().parse(temp.getString(tmpString), tmpNumber, pStatus);
228 if (U_FAILURE(pStatus)) {
229 continue;
230 }
231 } else if (temp.isNumeric()) {
232 tmpNumber = temp;
233 } else {
234 continue;
235 }
236 }
237 int32_t parseDistance = pos.getIndex() - oldPos;
238 if (parseDistance > longestParseDistance) {
239 if (pattern->getArgTypeCount() != 0) {
240 resultNumber = tmpNumber;
241 withNumberFormat = true;
242 } else {
243 withNumberFormat = false;
244 }
245 resultTimeUnit = i;
246 newPos = pos.getIndex();
247 longestParseDistance = parseDistance;
248 countOfLongestMatch = count;
249 }
250 }
251 }
252 }
253 /* After find the longest match, parse the number.
254 * Result number could be null for the pattern without number pattern.
255 * such as unit pattern in Arabic.
256 * When result number is null, use plural rule to set the number.
257 */
258 if (withNumberFormat == false && longestParseDistance != 0) {
259 // set the number using plurrual count
260 if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
261 resultNumber = Formattable(0.0);
262 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
263 resultNumber = Formattable(1.0);
264 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
265 resultNumber = Formattable(2.0);
266 } else {
267 // should not happen.
268 // TODO: how to handle?
269 resultNumber = Formattable(3.0);
270 }
271 }
272 if (longestParseDistance == 0) {
273 pos.setIndex(oldPos);
274 pos.setErrorIndex(0);
275 } else {
276 UErrorCode status = U_ZERO_ERROR;
277 LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status);
278 if (U_SUCCESS(status)) {
279 result.adoptObject(tmutamt.orphan());
280 pos.setIndex(newPos);
281 pos.setErrorIndex(-1);
282 } else {
283 pos.setIndex(oldPos);
284 pos.setErrorIndex(0);
285 }
286 }
287 }
288
289 void
create(UTimeUnitFormatStyle style,UErrorCode & status)290 TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
291 // fTimeUnitToCountToPatterns[] must have its elements initialized to NULL first
292 // before checking for failure status.
293 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
294 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
295 i = (TimeUnit::UTimeUnitFields)(i+1)) {
296 fTimeUnitToCountToPatterns[i] = NULL;
297 }
298
299 if (U_FAILURE(status)) {
300 return;
301 }
302 if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
303 status = U_ILLEGAL_ARGUMENT_ERROR;
304 return;
305 }
306 fStyle = style;
307
308 //TODO: format() and parseObj() are const member functions,
309 //so, can not do lazy initialization in C++.
310 //setup has to be done in constructors.
311 //and here, the behavior is not consistent with Java.
312 //In Java, create an empty instance does not setup locale as
313 //default locale. If it followed by setNumberFormat(),
314 //in format(), the locale will set up as the locale in fNumberFormat.
315 //But in C++, this sets the locale as the default locale.
316 setup(status);
317 }
318
319 void
setup(UErrorCode & err)320 TimeUnitFormat::setup(UErrorCode& err) {
321 initDataMembers(err);
322
323 UVector pluralCounts(0, uhash_compareUnicodeString, 6, err);
324 LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
325 if (U_FAILURE(err)) {
326 return;
327 }
328 UnicodeString* pluralCount;
329 while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) {
330 pluralCounts.addElement(pluralCount, err);
331 }
332 readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
333 checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
334 readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
335 checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
336 }
337
338
339 void
initDataMembers(UErrorCode & err)340 TimeUnitFormat::initDataMembers(UErrorCode& err){
341 if (U_FAILURE(err)) {
342 return;
343 }
344 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
345 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
346 i = (TimeUnit::UTimeUnitFields)(i+1)) {
347 deleteHash(fTimeUnitToCountToPatterns[i]);
348 fTimeUnitToCountToPatterns[i] = NULL;
349 }
350 }
351
352 struct TimeUnitFormatReadSink : public ResourceSink {
353 TimeUnitFormat *timeUnitFormatObj;
354 const UVector &pluralCounts;
355 UTimeUnitFormatStyle style;
356 UBool beenHere;
357
TimeUnitFormatReadSinkTimeUnitFormatReadSink358 TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
359 const UVector &pluralCounts, UTimeUnitFormatStyle style) :
360 timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts),
361 style(style), beenHere(FALSE){}
362
363 virtual ~TimeUnitFormatReadSink();
364
putTimeUnitFormatReadSink365 virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) {
366 // Skip all put() calls except the first one -- discard all fallback data.
367 if (beenHere) {
368 return;
369 } else {
370 beenHere = TRUE;
371 }
372
373 ResourceTable units = value.getTable(errorCode);
374 if (U_FAILURE(errorCode)) { return; }
375
376 for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) {
377 const char* timeUnitName = key;
378 if (timeUnitName == NULL) {
379 continue;
380 }
381
382 TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
383 if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
384 timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
385 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
386 timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
387 } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
388 timeUnitField = TimeUnit::UTIMEUNIT_DAY;
389 } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
390 timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
391 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
392 timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
393 } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
394 timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
395 } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
396 timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
397 } else {
398 continue;
399 }
400 LocalPointer<Hashtable> localCountToPatterns;
401 Hashtable *countToPatterns =
402 timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField];
403 if (countToPatterns == NULL) {
404 localCountToPatterns.adoptInsteadAndCheckErrorCode(
405 timeUnitFormatObj->initHash(errorCode), errorCode);
406 countToPatterns = localCountToPatterns.getAlias();
407 if (U_FAILURE(errorCode)) {
408 return;
409 }
410 }
411
412 ResourceTable countsToPatternTable = value.getTable(errorCode);
413 if (U_FAILURE(errorCode)) {
414 continue;
415 }
416 for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
417 errorCode = U_ZERO_ERROR;
418 UnicodeString pattern = value.getUnicodeString(errorCode);
419 if (U_FAILURE(errorCode)) {
420 continue;
421 }
422 UnicodeString pluralCountUniStr(key, -1, US_INV);
423 if (!pluralCounts.contains(&pluralCountUniStr)) {
424 continue;
425 }
426 LocalPointer<MessageFormat> messageFormat(new MessageFormat(
427 pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
428 if (U_FAILURE(errorCode)) {
429 return;
430 }
431 MessageFormat** formatters =
432 (MessageFormat**)countToPatterns->get(pluralCountUniStr);
433 if (formatters == NULL) {
434 LocalMemory<MessageFormat *> localFormatters(
435 (MessageFormat **)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
436 if (localFormatters.isNull()) {
437 errorCode = U_MEMORY_ALLOCATION_ERROR;
438 return;
439 }
440 localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
441 localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
442 countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
443 if (U_FAILURE(errorCode)) {
444 return;
445 }
446 formatters = localFormatters.orphan();
447 }
448 formatters[style] = messageFormat.orphan();
449 }
450
451 if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
452 timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
453 }
454 }
455 }
456
457 };
458
~TimeUnitFormatReadSink()459 TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
460
461 void
readFromCurrentLocale(UTimeUnitFormatStyle style,const char * key,const UVector & pluralCounts,UErrorCode & err)462 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
463 const UVector& pluralCounts, UErrorCode& err) {
464 if (U_FAILURE(err)) {
465 return;
466 }
467 // fill timeUnitToCountToPatterns from resource file
468 // err is used to indicate wrong status except missing resource.
469 // status is an error code used in resource lookup.
470 // status does not affect "err".
471 UErrorCode status = U_ZERO_ERROR;
472 LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status));
473
474 LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
475 ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
476 if (U_FAILURE(status)) {
477 return;
478 }
479
480 TimeUnitFormatReadSink sink(this, pluralCounts, style);
481 ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
482 }
483
484 void
checkConsistency(UTimeUnitFormatStyle style,const char * key,UErrorCode & err)485 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
486 if (U_FAILURE(err)) {
487 return;
488 }
489 // there should be patterns for each plural rule in each time unit.
490 // For each time unit,
491 // for each plural rule, following is unit pattern fall-back rule:
492 // ( for example: "one" hour )
493 // look for its unit pattern in its locale tree.
494 // if pattern is not found in its own locale, such as de_DE,
495 // look for the pattern in its parent, such as de,
496 // keep looking till found or till root.
497 // if the pattern is not found in root either,
498 // fallback to plural count "other",
499 // look for the pattern of "other" in the locale tree:
500 // "de_DE" to "de" to "root".
501 // If not found, fall back to value of
502 // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
503 //
504 // Following is consistency check to create pattern for each
505 // plural rule in each time unit using above fall-back rule.
506 //
507 LocalPointer<StringEnumeration> keywords(
508 getPluralRules().getKeywords(err), err);
509 const UnicodeString* pluralCount;
510 while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != NULL) {
511 for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
512 // for each time unit,
513 // get all the patterns for each plural rule in this locale.
514 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
515 if ( countToPatterns == NULL ) {
516 fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
517 if (U_FAILURE(err)) {
518 return;
519 }
520 }
521 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
522 if( formatters == NULL || formatters[style] == NULL ) {
523 // look through parents
524 const char* localeName = getLocaleID(err);
525 CharString pluralCountChars;
526 pluralCountChars.appendInvariantChars(*pluralCount, err);
527 searchInLocaleChain(style, key, localeName,
528 (TimeUnit::UTimeUnitFields)i,
529 *pluralCount, pluralCountChars.data(),
530 countToPatterns, err);
531 }
532 // TODO: what to do with U_FAILURE(err) at this point.
533 // As is, the outer loop continues to run, but does nothing.
534 }
535 }
536 }
537
538
539
540 // srcPluralCount is the original plural count on which the pattern is
541 // searched for.
542 // searchPluralCount is the fallback plural count.
543 // For example, to search for pattern for ""one" hour",
544 // "one" is the srcPluralCount,
545 // if the pattern is not found even in root, fallback to
546 // using patterns of plural count "other",
547 // then, "other" is the searchPluralCount.
548 void
searchInLocaleChain(UTimeUnitFormatStyle style,const char * key,const char * localeName,TimeUnit::UTimeUnitFields srcTimeUnitField,const UnicodeString & srcPluralCount,const char * searchPluralCount,Hashtable * countToPatterns,UErrorCode & err)549 TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
550 TimeUnit::UTimeUnitFields srcTimeUnitField,
551 const UnicodeString& srcPluralCount,
552 const char* searchPluralCount,
553 Hashtable* countToPatterns,
554 UErrorCode& err) {
555 if (U_FAILURE(err)) {
556 return;
557 }
558 UErrorCode status = U_ZERO_ERROR;
559 char parentLocale[ULOC_FULLNAME_CAPACITY];
560 uprv_strcpy(parentLocale, localeName);
561 int32_t locNameLen;
562 U_ASSERT(countToPatterns != NULL);
563 while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
564 ULOC_FULLNAME_CAPACITY, &status)) >= 0){
565 // look for pattern for srcPluralCount in locale tree
566 LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale, &status));
567 LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
568 const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
569 LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, NULL, &status));
570 const UChar* pattern;
571 int32_t ptLength;
572 pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
573 if (U_SUCCESS(status)) {
574 //found
575 LocalPointer<MessageFormat> messageFormat(
576 new MessageFormat(UnicodeString(TRUE, pattern, ptLength), getLocale(err), err), err);
577 if (U_FAILURE(err)) {
578 return;
579 }
580 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
581 if (formatters == NULL) {
582 LocalMemory<MessageFormat *> localFormatters(
583 (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
584 formatters = localFormatters.getAlias();
585 localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
586 localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
587 countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
588 if (U_FAILURE(err)) {
589 return;
590 }
591 }
592 //delete formatters[style];
593 formatters[style] = messageFormat.orphan();
594 return;
595 }
596 status = U_ZERO_ERROR;
597 if (locNameLen == 0) {
598 break;
599 }
600 }
601
602 // if no unitsShort resource was found even after fallback to root locale
603 // then search the units resource fallback from the current level to root
604 if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
605 #ifdef TMUTFMT_DEBUG
606 std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
607 #endif
608 CharString pLocale(localeName, -1, err);
609 // Add an underscore at the tail of locale name,
610 // so that searchInLocaleChain will check the current locale before falling back
611 pLocale.append('_', err);
612 searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount,
613 searchPluralCount, countToPatterns, err);
614 if (U_FAILURE(err)) {
615 return;
616 }
617 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
618 if (formatters != NULL && formatters[style] != NULL) {
619 return;
620 }
621 }
622
623 // if not found the pattern for this plural count at all,
624 // fall-back to plural count "other"
625 if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
626 // set default fall back the same as the resource in root
627 LocalPointer<MessageFormat> messageFormat;
628 const UChar *pattern = NULL;
629 if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
630 pattern = DEFAULT_PATTERN_FOR_SECOND;
631 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
632 pattern = DEFAULT_PATTERN_FOR_MINUTE;
633 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
634 pattern = DEFAULT_PATTERN_FOR_HOUR;
635 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
636 pattern = DEFAULT_PATTERN_FOR_WEEK;
637 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
638 pattern = DEFAULT_PATTERN_FOR_DAY;
639 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
640 pattern = DEFAULT_PATTERN_FOR_MONTH;
641 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
642 pattern = DEFAULT_PATTERN_FOR_YEAR;
643 }
644 if (pattern != NULL) {
645 messageFormat.adoptInsteadAndCheckErrorCode(
646 new MessageFormat(UnicodeString(TRUE, pattern, -1), getLocale(err), err), err);
647 }
648 if (U_FAILURE(err)) {
649 return;
650 }
651 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
652 if (formatters == NULL) {
653 LocalMemory<MessageFormat *> localFormatters (
654 (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
655 if (localFormatters.isNull()) {
656 err = U_MEMORY_ALLOCATION_ERROR;
657 return;
658 }
659 formatters = localFormatters.getAlias();
660 formatters[UTMUTFMT_FULL_STYLE] = NULL;
661 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
662 countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
663 }
664 if (U_SUCCESS(err)) {
665 //delete formatters[style];
666 formatters[style] = messageFormat.orphan();
667 }
668 } else {
669 // fall back to rule "other", and search in parents
670 searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
671 gPluralCountOther, countToPatterns, err);
672 }
673 }
674
675 void
setLocale(const Locale & locale,UErrorCode & status)676 TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
677 if (setMeasureFormatLocale(locale, status)) {
678 setup(status);
679 }
680 }
681
682
683 void
setNumberFormat(const NumberFormat & format,UErrorCode & status)684 TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
685 if (U_FAILURE(status)) {
686 return;
687 }
688 adoptNumberFormat((NumberFormat *)format.clone(), status);
689 }
690
691
692 void
deleteHash(Hashtable * htable)693 TimeUnitFormat::deleteHash(Hashtable* htable) {
694 int32_t pos = UHASH_FIRST;
695 const UHashElement* element = NULL;
696 if ( htable ) {
697 while ( (element = htable->nextElement(pos)) != NULL ) {
698 const UHashTok valueTok = element->value;
699 const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
700 delete value[UTMUTFMT_FULL_STYLE];
701 delete value[UTMUTFMT_ABBREVIATED_STYLE];
702 //delete[] value;
703 uprv_free(value);
704 }
705 }
706 delete htable;
707 }
708
709
710 void
copyHash(const Hashtable * source,Hashtable * target,UErrorCode & status)711 TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
712 if ( U_FAILURE(status) ) {
713 return;
714 }
715 int32_t pos = UHASH_FIRST;
716 const UHashElement* element = NULL;
717 if ( source ) {
718 while ( (element = source->nextElement(pos)) != NULL ) {
719 const UHashTok keyTok = element->key;
720 const UnicodeString* key = (UnicodeString*)keyTok.pointer;
721 const UHashTok valueTok = element->value;
722 const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
723 MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
724 newVal[0] = (MessageFormat*)value[0]->clone();
725 newVal[1] = (MessageFormat*)value[1]->clone();
726 target->put(UnicodeString(*key), newVal, status);
727 if ( U_FAILURE(status) ) {
728 delete newVal[0];
729 delete newVal[1];
730 uprv_free(newVal);
731 return;
732 }
733 }
734 }
735 }
736
737
738 U_CDECL_BEGIN
739
740 /**
741 * set hash table value comparator
742 *
743 * @param val1 one value in comparison
744 * @param val2 the other value in comparison
745 * @return TRUE if 2 values are the same, FALSE otherwise
746 */
747 static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
748
749 static UBool
tmutfmtHashTableValueComparator(UHashTok val1,UHashTok val2)750 U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
751 const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
752 const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
753 return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
754 }
755
756 U_CDECL_END
757
758 Hashtable*
initHash(UErrorCode & status)759 TimeUnitFormat::initHash(UErrorCode& status) {
760 if ( U_FAILURE(status) ) {
761 return NULL;
762 }
763 Hashtable* hTable;
764 if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
765 status = U_MEMORY_ALLOCATION_ERROR;
766 return NULL;
767 }
768 if ( U_FAILURE(status) ) {
769 delete hTable;
770 return NULL;
771 }
772 hTable->setValueComparator(tmutfmtHashTableValueComparator);
773 return hTable;
774 }
775
776
777 const char*
getTimeUnitName(TimeUnit::UTimeUnitFields unitField,UErrorCode & status)778 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
779 UErrorCode& status) {
780 if (U_FAILURE(status)) {
781 return NULL;
782 }
783 switch (unitField) {
784 case TimeUnit::UTIMEUNIT_YEAR:
785 return gTimeUnitYear;
786 case TimeUnit::UTIMEUNIT_MONTH:
787 return gTimeUnitMonth;
788 case TimeUnit::UTIMEUNIT_DAY:
789 return gTimeUnitDay;
790 case TimeUnit::UTIMEUNIT_WEEK:
791 return gTimeUnitWeek;
792 case TimeUnit::UTIMEUNIT_HOUR:
793 return gTimeUnitHour;
794 case TimeUnit::UTIMEUNIT_MINUTE:
795 return gTimeUnitMinute;
796 case TimeUnit::UTIMEUNIT_SECOND:
797 return gTimeUnitSecond;
798 default:
799 status = U_ILLEGAL_ARGUMENT_ERROR;
800 return NULL;
801 }
802 }
803
804 U_NAMESPACE_END
805
806 #endif
807