Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/icu/source/i18n/tmutfmt.cpp
Line
Count
Source (jump to first uncovered line)
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
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
0
    case UTMUTFMT_FULL_STYLE:
98
0
        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
99
0
        break;
100
0
    case UTMUTFMT_ABBREVIATED_STYLE:
101
0
        initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
102
0
        break;
103
0
    default:
104
0
        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
105
0
        break;
106
0
    }
107
0
    create(style, status);
108
0
}
109
110
TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
111
:   MeasureFormat(other),
112
    fStyle(other.fStyle)
113
0
{
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
0
        } else {
122
0
            delete fTimeUnitToCountToPatterns[i];
123
0
            fTimeUnitToCountToPatterns[i] = NULL;
124
0
        }
125
0
    }
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
0
    }
136
0
}
137
138
139
Format*
140
0
TimeUnitFormat::clone(void) const {
141
0
    return new TimeUnitFormat(*this);
142
0
}
143
144
145
TimeUnitFormat&
146
0
TimeUnitFormat::operator=(const TimeUnitFormat& other) {
147
0
    if (this == &other) {
148
0
        return *this;
149
0
    }
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
0
    }
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
0
        } else {
165
0
            delete fTimeUnitToCountToPatterns[i];
166
0
            fTimeUnitToCountToPatterns[i] = NULL;
167
0
        }
168
0
    }
169
0
    fStyle = other.fStyle;
170
0
    return *this;
171
0
}
172
173
void
174
TimeUnitFormat::parseObject(const UnicodeString& source,
175
                            Formattable& result,
176
0
                            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
0
    // 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
            const UHashTok valueTok = elem->value;
205
0
            // 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
0
                // 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
0
                }
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
0
                        }
231
0
                    } else if (temp.isNumeric()) {
232
0
                        tmpNumber = temp;
233
0
                    } else {
234
0
                        continue;
235
0
                    }
236
0
                }
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
0
                    } else {
243
0
                        withNumberFormat = false;
244
0
                    }
245
0
                    resultTimeUnit = i;
246
0
                    newPos = pos.getIndex();
247
0
                    longestParseDistance = parseDistance;
248
0
                    countOfLongestMatch = count;
249
0
                }
250
0
            }
251
0
        }
252
0
    }
253
0
    /* After find the longest match, parse the number.
254
0
     * Result number could be null for the pattern without number pattern.
255
0
     * such as unit pattern in Arabic.
256
0
     * When result number is null, use plural rule to set the number.
257
0
     */
258
0
    if (withNumberFormat == false && longestParseDistance != 0) {
259
0
        // 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
0
        } else {
267
0
            // should not happen.
268
0
            // TODO: how to handle?
269
0
            resultNumber = Formattable(3.0);
270
0
        }
271
0
    }
272
0
    if (longestParseDistance == 0) {
273
0
        pos.setIndex(oldPos);
274
0
        pos.setErrorIndex(0);
275
0
    } 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
0
        } else {
283
0
            pos.setIndex(oldPos);
284
0
            pos.setErrorIndex(0);
285
0
        }
286
0
    }
287
0
}
288
289
void
290
0
TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
291
0
    // fTimeUnitToCountToPatterns[] must have its elements initialized to NULL first
292
0
    // 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
0
    }
298
0
299
0
    if (U_FAILURE(status)) {
300
0
        return;
301
0
    }
302
0
    if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
303
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
304
0
        return;
305
0
    }
306
0
    fStyle = style;
307
0
308
0
    //TODO: format() and parseObj() are const member functions,
309
0
    //so, can not do lazy initialization in C++.
310
0
    //setup has to be done in constructors.
311
0
    //and here, the behavior is not consistent with Java.
312
0
    //In Java, create an empty instance does not setup locale as
313
0
    //default locale. If it followed by setNumberFormat(),
314
0
    //in format(), the locale will set up as the locale in fNumberFormat.
315
0
    //But in C++, this sets the locale as the default locale.
316
0
    setup(status);
317
0
}
318
319
void
320
0
TimeUnitFormat::setup(UErrorCode& err) {
321
0
    initDataMembers(err);
322
0
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
0
    }
328
0
    UnicodeString* pluralCount;
329
0
    while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) {
330
0
      pluralCounts.addElement(pluralCount, err);
331
0
    }
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
0
}
337
338
339
void
340
0
TimeUnitFormat::initDataMembers(UErrorCode& err){
341
0
    if (U_FAILURE(err)) {
342
0
        return;
343
0
    }
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
0
    }
350
0
}
351
352
struct TimeUnitFormatReadSink : public ResourceSink {
353
    TimeUnitFormat *timeUnitFormatObj;
354
    const UVector &pluralCounts;
355
    UTimeUnitFormatStyle style;
356
    UBool beenHere;
357
358
    TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
359
            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
0
        // Skip all put() calls except the first one -- discard all fallback data.
367
0
        if (beenHere) {
368
0
            return;
369
0
        } else {
370
0
            beenHere = TRUE;
371
0
        }
372
0
373
0
        ResourceTable units = value.getTable(errorCode);
374
0
        if (U_FAILURE(errorCode)) { return; }
375
0
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
0
            }
381
0
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
0
            } else {
398
0
                continue;
399
0
            }
400
0
            LocalPointer<Hashtable> localCountToPatterns;
401
0
            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
0
                }
410
0
            }
411
0
412
0
            ResourceTable countsToPatternTable = value.getTable(errorCode);
413
0
            if (U_FAILURE(errorCode)) {
414
0
                continue;
415
0
            }
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
0
                }
422
0
                UnicodeString pluralCountUniStr(key, -1, US_INV);
423
0
                if (!pluralCounts.contains(&pluralCountUniStr)) {
424
0
                    continue;
425
0
                }
426
0
                LocalPointer<MessageFormat> messageFormat(new MessageFormat(
427
0
                    pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
428
0
                if (U_FAILURE(errorCode)) {
429
0
                    return;
430
0
                }
431
0
                MessageFormat** formatters =
432
0
                    (MessageFormat**)countToPatterns->get(pluralCountUniStr);
433
0
                if (formatters == NULL) {
434
0
                    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
0
                    }
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
0
                    }
446
0
                    formatters = localFormatters.orphan();
447
0
                }
448
0
                formatters[style] = messageFormat.orphan();
449
0
            }
450
0
451
0
            if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
452
0
                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
453
0
            }
454
0
        }
455
0
    }
456
457
};
458
459
0
TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
460
461
void
462
TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
463
0
                                      const UVector& pluralCounts, UErrorCode& err) {
464
0
    if (U_FAILURE(err)) {
465
0
        return;
466
0
    }
467
0
    // fill timeUnitToCountToPatterns from resource file
468
0
    // err is used to indicate wrong status except missing resource.
469
0
    // status is an error code used in resource lookup.
470
0
    // 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
0
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
0
    }
479
0
480
0
    TimeUnitFormatReadSink sink(this, pluralCounts, style);
481
0
    ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
482
0
}
483
484
void
485
0
TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
486
0
    if (U_FAILURE(err)) {
487
0
        return;
488
0
    }
489
0
    // there should be patterns for each plural rule in each time unit.
490
0
    // For each time unit,
491
0
    //     for each plural rule, following is unit pattern fall-back rule:
492
0
    //         ( for example: "one" hour )
493
0
    //         look for its unit pattern in its locale tree.
494
0
    //         if pattern is not found in its own locale, such as de_DE,
495
0
    //         look for the pattern in its parent, such as de,
496
0
    //         keep looking till found or till root.
497
0
    //         if the pattern is not found in root either,
498
0
    //         fallback to plural count "other",
499
0
    //         look for the pattern of "other" in the locale tree:
500
0
    //         "de_DE" to "de" to "root".
501
0
    //         If not found, fall back to value of
502
0
    //         static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
503
0
    //
504
0
    // Following is consistency check to create pattern for each
505
0
    // plural rule in each time unit using above fall-back rule.
506
0
    //
507
0
    LocalPointer<StringEnumeration> keywords(
508
0
            getPluralRules().getKeywords(err), err);
509
0
    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
0
            // for each time unit,
513
0
            // 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
0
                }
520
0
            }
521
0
            MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
522
0
            if( formatters == NULL || formatters[style] == NULL ) {
523
0
                // 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
0
                                    (TimeUnit::UTimeUnitFields)i,
529
0
                                    *pluralCount, pluralCountChars.data(),
530
0
                                    countToPatterns, err);
531
0
            }
532
0
            // TODO: what to do with U_FAILURE(err) at this point.
533
0
            //       As is, the outer loop continues to run, but does nothing.
534
0
        }
535
0
    }
536
0
}
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
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
0
                                UErrorCode& err) {
555
0
    if (U_FAILURE(err)) {
556
0
        return;
557
0
    }
558
0
    UErrorCode status = U_ZERO_ERROR;
559
0
    char parentLocale[ULOC_FULLNAME_CAPACITY];
560
0
    uprv_strcpy(parentLocale, localeName);
561
0
    int32_t locNameLen;
562
0
    U_ASSERT(countToPatterns != NULL);
563
0
    while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
564
0
                                        ULOC_FULLNAME_CAPACITY, &status)) >= 0){
565
0
        // 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
0
        const UChar* pattern;
571
0
        int32_t      ptLength;
572
0
        pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
573
0
        if (U_SUCCESS(status)) {
574
0
            //found
575
0
            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
0
            }
580
0
            MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
581
0
            if (formatters == NULL) {
582
0
                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
0
                }
591
0
            }
592
0
            //delete formatters[style];
593
0
            formatters[style] = messageFormat.orphan();
594
0
            return;
595
0
        }
596
0
        status = U_ZERO_ERROR;
597
0
        if (locNameLen == 0) {
598
0
            break;
599
0
        }
600
0
    }
601
0
602
0
    // if no unitsShort resource was found even after fallback to root locale
603
0
    // 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
        CharString pLocale(localeName, -1, err);
609
0
        // Add an underscore at the tail of locale name,
610
0
        // 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
0
        }
617
0
        MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
618
0
        if (formatters != NULL && formatters[style] != NULL) {
619
0
            return;
620
0
        }
621
0
    }
622
0
623
0
    // if not found the pattern for this plural count at all,
624
0
    // fall-back to plural count "other"
625
0
    if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
626
0
        // 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
0
        }
644
0
        if (pattern != NULL) {
645
0
            messageFormat.adoptInsteadAndCheckErrorCode(
646
0
                     new MessageFormat(UnicodeString(TRUE, pattern, -1), getLocale(err), err), err);
647
0
        }
648
0
        if (U_FAILURE(err)) {
649
0
            return;
650
0
        }
651
0
        MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
652
0
        if (formatters == NULL) {
653
0
            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
0
            }
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
0
        }
664
0
        if (U_SUCCESS(err)) {
665
0
            //delete formatters[style];
666
0
            formatters[style] = messageFormat.orphan();
667
0
        }
668
0
    } else {
669
0
        // fall back to rule "other", and search in parents
670
0
        searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
671
0
                            gPluralCountOther, countToPatterns, err);
672
0
    }
673
0
}
674
675
void
676
0
TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
677
0
    if (setMeasureFormatLocale(locale, status)) {
678
0
        setup(status);
679
0
    }
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
0
    }
688
0
    adoptNumberFormat((NumberFormat *)format.clone(), status);
689
0
}
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
0
            //delete[] value;
703
0
            uprv_free(value);
704
0
        }
705
0
    }
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
0
    }
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
0
            }
733
0
        }
734
0
    }
735
0
}
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
0
}
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
0
    }
763
0
    Hashtable* hTable;
764
0
    if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
765
0
        status = U_MEMORY_ALLOCATION_ERROR;
766
0
        return NULL;
767
0
    }
768
0
    if ( U_FAILURE(status) ) {
769
0
        delete hTable;
770
0
        return NULL;
771
0
    }
772
0
    hTable->setValueComparator(tmutfmtHashTableValueComparator);
773
0
    return hTable;
774
0
}
775
776
777
const char*
778
TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
779
0
                                UErrorCode& status) {
780
0
    if (U_FAILURE(status)) {
781
0
        return NULL;
782
0
    }
783
0
    switch (unitField) {
784
0
      case TimeUnit::UTIMEUNIT_YEAR:
785
0
        return gTimeUnitYear;
786
0
      case TimeUnit::UTIMEUNIT_MONTH:
787
0
        return gTimeUnitMonth;
788
0
      case TimeUnit::UTIMEUNIT_DAY:
789
0
        return gTimeUnitDay;
790
0
      case TimeUnit::UTIMEUNIT_WEEK:
791
0
        return gTimeUnitWeek;
792
0
      case TimeUnit::UTIMEUNIT_HOUR:
793
0
        return gTimeUnitHour;
794
0
      case TimeUnit::UTIMEUNIT_MINUTE:
795
0
        return gTimeUnitMinute;
796
0
      case TimeUnit::UTIMEUNIT_SECOND:
797
0
        return gTimeUnitSecond;
798
0
      default:
799
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
800
0
        return NULL;
801
0
    }
802
0
}
803
804
U_NAMESPACE_END
805
806
#endif