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