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