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