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