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