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