Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/icu/source/i18n/dtitvinf.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
* Copyright (C) 2008-2016, International Business Machines Corporation and
5
* others. All Rights Reserved.
6
*******************************************************************************
7
*
8
* File DTITVINF.CPP
9
*
10
*******************************************************************************
11
*/
12
13
#include "unicode/dtitvinf.h"
14
15
16
#if !UCONFIG_NO_FORMATTING
17
18
//TODO: define it in compiler time
19
//#define DTITVINF_DEBUG 1
20
21
22
#ifdef DTITVINF_DEBUG
23
#include <iostream>
24
#endif
25
26
#include "cmemory.h"
27
#include "cstring.h"
28
#include "unicode/msgfmt.h"
29
#include "unicode/uloc.h"
30
#include "unicode/ures.h"
31
#include "dtitv_impl.h"
32
#include "charstr.h"
33
#include "hash.h"
34
#include "gregoimp.h"
35
#include "uresimp.h"
36
#include "hash.h"
37
#include "gregoimp.h"
38
#include "uresimp.h"
39
40
41
U_NAMESPACE_BEGIN
42
43
44
#ifdef DTITVINF_DEBUG
45
#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
46
#endif
47
48
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
49
50
static const char gCalendarTag[]="calendar";
51
static const char gGregorianTag[]="gregorian";
52
static const char gIntervalDateTimePatternTag[]="intervalFormats";
53
static const char gFallbackPatternTag[]="fallback";
54
55
// {0}
56
static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
57
// {1}
58
static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
59
60
// default fall-back
61
static const UChar gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0};
62
63
DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
64
:   fFallbackIntervalPattern(gDefaultFallbackPattern),
65
    fFirstDateInPtnIsLaterDate(false),
66
    fIntervalPatterns(NULL)
67
0
{
68
0
    fIntervalPatterns = initHash(status);
69
0
}
70
71
72
73
DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
74
:   fFallbackIntervalPattern(gDefaultFallbackPattern),
75
    fFirstDateInPtnIsLaterDate(false),
76
    fIntervalPatterns(NULL)
77
0
{
78
0
    initializeData(locale, status);
79
0
}
80
81
82
83
void
84
DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
85
                                     UCalendarDateFields lrgDiffCalUnit,
86
                                     const UnicodeString& intervalPattern,
87
0
                                     UErrorCode& status) {
88
0
89
0
    if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) {
90
0
        setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status);
91
0
        setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status);
92
0
    } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH ||
93
0
                lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) {
94
0
        setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status);
95
0
    } else {
96
0
        setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
97
0
    }
98
0
}
99
100
101
void
102
DateIntervalInfo::setFallbackIntervalPattern(
103
                                    const UnicodeString& fallbackPattern,
104
0
                                    UErrorCode& status) {
105
0
    if ( U_FAILURE(status) ) {
106
0
        return;
107
0
    }
108
0
    int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
109
0
                        UPRV_LENGTHOF(gFirstPattern), 0);
110
0
    int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
111
0
                        UPRV_LENGTHOF(gSecondPattern), 0);
112
0
    if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
113
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
114
0
        return;
115
0
    }
116
0
    if ( firstPatternIndex > secondPatternIndex ) {
117
0
        fFirstDateInPtnIsLaterDate = true;
118
0
    }
119
0
    fFallbackIntervalPattern = fallbackPattern;
120
0
}
121
122
123
124
DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
125
:   UObject(dtitvinf),
126
    fIntervalPatterns(NULL)
127
0
{
128
0
    *this = dtitvinf;
129
0
}
130
131
132
133
DateIntervalInfo&
134
0
DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
135
0
    if ( this == &dtitvinf ) {
136
0
        return *this;
137
0
    }
138
0
139
0
    UErrorCode status = U_ZERO_ERROR;
140
0
    deleteHash(fIntervalPatterns);
141
0
    fIntervalPatterns = initHash(status);
142
0
    copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
143
0
    if ( U_FAILURE(status) ) {
144
0
        return *this;
145
0
    }
146
0
147
0
    fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
148
0
    fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
149
0
    return *this;
150
0
}
151
152
153
DateIntervalInfo*
154
0
DateIntervalInfo::clone() const {
155
0
    return new DateIntervalInfo(*this);
156
0
}
157
158
159
0
DateIntervalInfo::~DateIntervalInfo() {
160
0
    deleteHash(fIntervalPatterns);
161
0
    fIntervalPatterns = NULL;
162
0
}
163
164
165
UBool
166
0
DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
167
0
    UBool equal = (
168
0
      fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
169
0
      fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
170
0
171
0
    if ( equal == TRUE ) {
172
0
        equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
173
0
    }
174
0
175
0
    return equal;
176
0
}
177
178
179
UnicodeString&
180
DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
181
                                     UCalendarDateFields field,
182
                                     UnicodeString& result,
183
0
                                     UErrorCode& status) const {
184
0
    if ( U_FAILURE(status) ) {
185
0
        return result;
186
0
    }
187
0
188
0
    const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
189
0
    if ( patternsOfOneSkeleton != NULL ) {
190
0
        IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
191
0
        if ( U_FAILURE(status) ) {
192
0
            return result;
193
0
        }
194
0
        const UnicodeString& intervalPattern =  patternsOfOneSkeleton[index];
195
0
        if ( !intervalPattern.isEmpty() ) {
196
0
            result = intervalPattern;
197
0
        }
198
0
    }
199
0
    return result;
200
0
}
201
202
203
UBool
204
0
DateIntervalInfo::getDefaultOrder() const {
205
0
    return fFirstDateInPtnIsLaterDate;
206
0
}
207
208
209
UnicodeString&
210
0
DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
211
0
    result = fFallbackIntervalPattern;
212
0
    return result;
213
0
}
214
215
0
#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
216
217
218
static const int32_t PATH_PREFIX_LENGTH = 17;
219
static const UChar PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS,
220
                                    LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS};
221
static const int32_t PATH_SUFFIX_LENGTH = 16;
222
static const UChar PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A,
223
                                    LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S};
224
225
/**
226
 * Sink for enumerating all of the date interval skeletons.
227
 */
228
struct DateIntervalInfo::DateIntervalSink : public ResourceSink {
229
230
    // Output data
231
    DateIntervalInfo &dateIntervalInfo;
232
233
    // Next calendar type
234
    UnicodeString nextCalendarType;
235
236
    DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
237
0
            : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { }
238
    virtual ~DateIntervalSink();
239
240
0
    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) {
241
0
        if (U_FAILURE(errorCode)) { return; }
242
0
243
0
        // Iterate over all the calendar entries and only pick the 'intervalFormats' table.
244
0
        ResourceTable dateIntervalData = value.getTable(errorCode);
245
0
        if (U_FAILURE(errorCode)) { return; }
246
0
        for (int32_t i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) {
247
0
            if (uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) {
248
0
                continue;
249
0
            }
250
0
251
0
            // Handle aliases and tables. Ignore the rest.
252
0
            if (value.getType() == URES_ALIAS) {
253
0
                // Get the calendar type for the alias path.
254
0
                const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode);
255
0
                if (U_FAILURE(errorCode)) { return; }
256
0
257
0
                nextCalendarType.remove();
258
0
                getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
259
0
260
0
                if (U_FAILURE(errorCode)) {
261
0
                    resetNextCalendarType();
262
0
                }
263
0
                break;
264
0
265
0
            } else if (value.getType() == URES_TABLE) {
266
0
                // Iterate over all the skeletons in the 'intervalFormat' table.
267
0
                ResourceTable skeletonData = value.getTable(errorCode);
268
0
                if (U_FAILURE(errorCode)) { return; }
269
0
                for (int32_t j = 0; skeletonData.getKeyAndValue(j, key, value); j++) {
270
0
                    if (value.getType() == URES_TABLE) {
271
0
                        // Process the skeleton
272
0
                        processSkeletonTable(key, value, errorCode);
273
0
                        if (U_FAILURE(errorCode)) { return; }
274
0
                    }
275
0
                }
276
0
                break;
277
0
            }
278
0
        }
279
0
    }
280
281
    /**
282
     * Processes the patterns for a skeleton table
283
     */
284
0
    void processSkeletonTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
285
0
        if (U_FAILURE(errorCode)) { return; }
286
0
287
0
        // Iterate over all the patterns in the current skeleton table
288
0
        const char *currentSkeleton = key;
289
0
        ResourceTable patternData = value.getTable(errorCode);
290
0
        if (U_FAILURE(errorCode)) { return; }
291
0
        for (int32_t k = 0; patternData.getKeyAndValue(k, key, value); k++) {
292
0
            if (value.getType() == URES_STRING) {
293
0
                // Process the key
294
0
                UCalendarDateFields calendarField = validateAndProcessPatternLetter(key);
295
0
296
0
                // If the calendar field has a valid value
297
0
                if (calendarField < UCAL_FIELD_COUNT) {
298
0
                    // Set the interval pattern
299
0
                    setIntervalPatternIfAbsent(currentSkeleton, calendarField, value, errorCode);
300
0
                    if (U_FAILURE(errorCode)) { return; }
301
0
                }
302
0
            }
303
0
        }
304
0
    }
305
306
    /**
307
     * Extracts the calendar type from the path.
308
     */
309
    static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType,
310
0
                                        UErrorCode &errorCode) {
311
0
        if (U_FAILURE(errorCode)) { return; }
312
0
313
0
        if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) {
314
0
            errorCode = U_INVALID_FORMAT_ERROR;
315
0
            return;
316
0
        }
317
0
318
0
        path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType);
319
0
    }
320
321
    /**
322
     * Validates and processes the pattern letter
323
     */
324
0
    UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) {
325
0
        // Check that patternLetter is just one letter
326
0
        char c0;
327
0
        if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
328
0
            // Check that the pattern letter is accepted
329
0
            if (c0 == 'y') {
330
0
                return UCAL_YEAR;
331
0
            } else if (c0 == 'M') {
332
0
                return UCAL_MONTH;
333
0
            } else if (c0 == 'd') {
334
0
                return UCAL_DATE;
335
0
            } else if (c0 == 'a') {
336
0
                return UCAL_AM_PM;
337
0
            } else if (c0 == 'h' || c0 == 'H') {
338
0
                return UCAL_HOUR;
339
0
            } else if (c0 == 'm') {
340
0
                return UCAL_MINUTE;
341
0
            }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
342
0
        }
343
0
        return UCAL_FIELD_COUNT;
344
0
    }
345
346
    /**
347
     * Stores the interval pattern for the current skeleton in the internal data structure
348
     * if it's not present.
349
     */
350
    void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit,
351
0
                                    const ResourceValue &value, UErrorCode &errorCode) {
352
0
        // Check if the pattern has already been stored on the data structure
353
0
        IntervalPatternIndex index =
354
0
            dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
355
0
        if (U_FAILURE(errorCode)) { return; }
356
0
357
0
        UnicodeString skeleton(currentSkeleton, -1, US_INV);
358
0
        UnicodeString* patternsOfOneSkeleton =
359
0
            (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton));
360
0
361
0
        if (patternsOfOneSkeleton == NULL || patternsOfOneSkeleton[index].isEmpty()) {
362
0
            UnicodeString pattern = value.getUnicodeString(errorCode);
363
0
            dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
364
0
                                                          pattern, errorCode);
365
0
        }
366
0
    }
367
368
0
    const UnicodeString &getNextCalendarType() {
369
0
        return nextCalendarType;
370
0
    }
371
372
0
    void resetNextCalendarType() {
373
0
        nextCalendarType.setToBogus();
374
0
    }
375
};
376
377
// Virtual destructors must be defined out of line.
378
0
DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
379
380
381
382
void
383
DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
384
0
{
385
0
    fIntervalPatterns = initHash(status);
386
0
    if (U_FAILURE(status)) {
387
0
      return;
388
0
    }
389
0
    const char *locName = locale.getName();
390
0
391
0
    // Get the correct calendar type
392
0
    const char * calendarTypeToUse = gGregorianTag; // initial default
393
0
    char         calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
394
0
    char         localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
395
0
    // obtain a locale that always has the calendar key value that should be used
396
0
    (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL,
397
0
                                     "calendar", "calendar", locName, NULL, FALSE, &status);
398
0
    localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
399
0
    // now get the calendar key value from that locale
400
0
    int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType,
401
0
                                                   ULOC_KEYWORDS_CAPACITY, &status);
402
0
    if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
403
0
        calendarTypeToUse = calendarType;
404
0
    }
405
0
    status = U_ZERO_ERROR;
406
0
407
0
    // Instantiate the resource bundles
408
0
    UResourceBundle *rb, *calBundle;
409
0
    rb = ures_open(NULL, locName, &status);
410
0
    if (U_FAILURE(status)) {
411
0
        return;
412
0
    }
413
0
    calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, NULL, &status);
414
0
415
0
416
0
    if (U_SUCCESS(status)) {
417
0
        UResourceBundle *calTypeBundle, *itvDtPtnResource;
418
0
419
0
        // Get the fallback pattern
420
0
        const UChar* resStr;
421
0
        int32_t resStrLen = 0;
422
0
        calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &status);
423
0
        itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
424
0
                                                     gIntervalDateTimePatternTag, NULL, &status);
425
0
        resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag,
426
0
                                                 &resStrLen, &status);
427
0
        if ( U_SUCCESS(status) ) {
428
0
            UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
429
0
            setFallbackIntervalPattern(pattern, status);
430
0
        }
431
0
        ures_close(itvDtPtnResource);
432
0
        ures_close(calTypeBundle);
433
0
434
0
435
0
        // Instantiate the sink
436
0
        DateIntervalSink sink(*this, calendarTypeToUse);
437
0
        const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
438
0
439
0
        // Already loaded calendar types
440
0
        Hashtable loadedCalendarTypes(FALSE, status);
441
0
442
0
        if (U_SUCCESS(status)) {
443
0
            while (!calendarTypeToUseUString.isBogus()) {
444
0
                // Set an error when a loop is detected
445
0
                if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
446
0
                    status = U_INVALID_FORMAT_ERROR;
447
0
                    break;
448
0
                }
449
0
450
0
                // Register the calendar type to avoid loops
451
0
                loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
452
0
                if (U_FAILURE(status)) { break; }
453
0
454
0
                // Get the calendar string
455
0
                CharString calTypeBuffer;
456
0
                calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
457
0
                if (U_FAILURE(status)) { break; }
458
0
                const char *calType = calTypeBuffer.data();
459
0
460
0
                // Reset the next calendar type to load.
461
0
                sink.resetNextCalendarType();
462
0
463
0
                // Get all resources for this calendar type
464
0
                ures_getAllItemsWithFallback(calBundle, calType, sink, status);
465
0
            }
466
0
        }
467
0
    }
468
0
469
0
    // Close the opened resource bundles
470
0
    ures_close(calBundle);
471
0
    ures_close(rb);
472
0
}
473
474
void
475
DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
476
                                      UCalendarDateFields lrgDiffCalUnit,
477
                                      const UnicodeString& intervalPattern,
478
0
                                      UErrorCode& status) {
479
0
    IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
480
0
    if ( U_FAILURE(status) ) {
481
0
        return;
482
0
    }
483
0
    UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
484
0
    UBool emptyHash = false;
485
0
    if ( patternsOfOneSkeleton == NULL ) {
486
0
        patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
487
0
        emptyHash = true;
488
0
    }
489
0
490
0
    patternsOfOneSkeleton[index] = intervalPattern;
491
0
    if ( emptyHash == TRUE ) {
492
0
        fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
493
0
    }
494
0
}
495
496
497
498
void
499
DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
500
0
                                int32_t* skeletonFieldWidth) {
501
0
    const int8_t PATTERN_CHAR_BASE = 0x41;
502
0
    int32_t i;
503
0
    for ( i = 0; i < skeleton.length(); ++i ) {
504
0
        // it is an ASCII char in skeleton
505
0
        int8_t ch = (int8_t)skeleton.charAt(i);
506
0
        ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
507
0
    }
508
0
}
509
510
511
512
UBool
513
DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
514
0
                                char patternLetter) {
515
0
    if ( patternLetter == 'M' ) {
516
0
        if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
517
0
             (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
518
0
            return true;
519
0
        }
520
0
    }
521
0
    return false;
522
0
}
523
524
525
526
const UnicodeString*
527
DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
528
0
                                  int8_t& bestMatchDistanceInfo) const {
529
#ifdef DTITVINF_DEBUG
530
    char result[1000];
531
    char result_1[1000];
532
    char mesg[2000];
533
    skeleton.extract(0,  skeleton.length(), result, "UTF-8");
534
    sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result);
535
    PRINTMESG(mesg)
536
#endif
537
538
0
539
0
    int32_t inputSkeletonFieldWidth[] =
540
0
    {
541
0
    //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
542
0
             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
543
0
    //   P   Q   R   S   T   U   V   W   X   Y   Z
544
0
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
545
0
    //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
546
0
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
547
0
    //   p   q   r   s   t   u   v   w   x   y   z
548
0
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
549
0
    };
550
0
551
0
    int32_t skeletonFieldWidth[] =
552
0
    {
553
0
    //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
554
0
             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
555
0
    //   P   Q   R   S   T   U   V   W   X   Y   Z
556
0
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
557
0
    //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
558
0
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
559
0
    //   p   q   r   s   t   u   v   w   x   y   z
560
0
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
561
0
    };
562
0
563
0
    const int32_t DIFFERENT_FIELD = 0x1000;
564
0
    const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
565
0
    const int32_t BASE = 0x41;
566
0
    const UChar CHAR_V = 0x0076;
567
0
    const UChar CHAR_Z = 0x007A;
568
0
569
0
    // hack for 'v' and 'z'.
570
0
    // resource bundle only have time skeletons ending with 'v',
571
0
    // but not for time skeletons ending with 'z'.
572
0
    UBool replaceZWithV = false;
573
0
    const UnicodeString* inputSkeleton = &skeleton;
574
0
    UnicodeString copySkeleton;
575
0
    if ( skeleton.indexOf(CHAR_Z) != -1 ) {
576
0
        copySkeleton = skeleton;
577
0
        copySkeleton.findAndReplace(UnicodeString(CHAR_Z), UnicodeString(CHAR_V));
578
0
        inputSkeleton = &copySkeleton;
579
0
        replaceZWithV = true;
580
0
    }
581
0
582
0
    parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
583
0
    int32_t bestDistance = MAX_POSITIVE_INT;
584
0
    const UnicodeString* bestSkeleton = NULL;
585
0
586
0
    // 0 means exact the same skeletons;
587
0
    // 1 means having the same field, but with different length,
588
0
    // 2 means only z/v differs
589
0
    // -1 means having different field.
590
0
    bestMatchDistanceInfo = 0;
591
0
    int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
592
0
593
0
    int32_t pos = UHASH_FIRST;
594
0
    const UHashElement* elem = NULL;
595
0
    while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) {
596
0
        const UHashTok keyTok = elem->key;
597
0
        UnicodeString* skeleton = (UnicodeString*)keyTok.pointer;
598
#ifdef DTITVINF_DEBUG
599
    skeleton->extract(0,  skeleton->length(), result, "UTF-8");
600
    sprintf(mesg, "available skeletons: skeleton: %s; \n", result);
601
    PRINTMESG(mesg)
602
#endif
603
604
0
        // clear skeleton field width
605
0
        int8_t i;
606
0
        for ( i = 0; i < fieldLength; ++i ) {
607
0
            skeletonFieldWidth[i] = 0;
608
0
        }
609
0
        parseSkeleton(*skeleton, skeletonFieldWidth);
610
0
        // calculate distance
611
0
        int32_t distance = 0;
612
0
        int8_t fieldDifference = 1;
613
0
        for ( i = 0; i < fieldLength; ++i ) {
614
0
            int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
615
0
            int32_t fieldWidth = skeletonFieldWidth[i];
616
0
            if ( inputFieldWidth == fieldWidth ) {
617
0
                continue;
618
0
            }
619
0
            if ( inputFieldWidth == 0 ) {
620
0
                fieldDifference = -1;
621
0
                distance += DIFFERENT_FIELD;
622
0
            } else if ( fieldWidth == 0 ) {
623
0
                fieldDifference = -1;
624
0
                distance += DIFFERENT_FIELD;
625
0
            } else if (stringNumeric(inputFieldWidth, fieldWidth,
626
0
                                     (char)(i+BASE) ) ) {
627
0
                distance += STRING_NUMERIC_DIFFERENCE;
628
0
            } else {
629
0
                distance += (inputFieldWidth > fieldWidth) ?
630
0
                            (inputFieldWidth - fieldWidth) :
631
0
                            (fieldWidth - inputFieldWidth);
632
0
            }
633
0
        }
634
0
        if ( distance < bestDistance ) {
635
0
            bestSkeleton = skeleton;
636
0
            bestDistance = distance;
637
0
            bestMatchDistanceInfo = fieldDifference;
638
0
        }
639
0
        if ( distance == 0 ) {
640
0
            bestMatchDistanceInfo = 0;
641
0
            break;
642
0
        }
643
0
    }
644
0
    if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
645
0
        bestMatchDistanceInfo = 2;
646
0
    }
647
0
    return bestSkeleton;
648
0
}
649
650
651
652
DateIntervalInfo::IntervalPatternIndex
653
DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
654
0
                                               UErrorCode& status) {
655
0
    if ( U_FAILURE(status) ) {
656
0
        return kIPI_MAX_INDEX;
657
0
    }
658
0
    IntervalPatternIndex index = kIPI_MAX_INDEX;
659
0
    switch ( field ) {
660
0
      case UCAL_ERA:
661
0
        index = kIPI_ERA;
662
0
        break;
663
0
      case UCAL_YEAR:
664
0
        index = kIPI_YEAR;
665
0
        break;
666
0
      case UCAL_MONTH:
667
0
        index = kIPI_MONTH;
668
0
        break;
669
0
      case UCAL_DATE:
670
0
      case UCAL_DAY_OF_WEEK:
671
0
      //case UCAL_DAY_OF_MONTH:
672
0
        index = kIPI_DATE;
673
0
        break;
674
0
      case UCAL_AM_PM:
675
0
        index = kIPI_AM_PM;
676
0
        break;
677
0
      case UCAL_HOUR:
678
0
      case UCAL_HOUR_OF_DAY:
679
0
        index = kIPI_HOUR;
680
0
        break;
681
0
      case UCAL_MINUTE:
682
0
        index = kIPI_MINUTE;
683
0
        break;
684
0
      case UCAL_SECOND:
685
0
        index = kIPI_SECOND;
686
0
        break;
687
0
      default:
688
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
689
0
    }
690
0
    return index;
691
0
}
692
693
694
695
void
696
DateIntervalInfo::deleteHash(Hashtable* hTable)
697
0
{
698
0
    if ( hTable == NULL ) {
699
0
        return;
700
0
    }
701
0
    int32_t pos = UHASH_FIRST;
702
0
    const UHashElement* element = NULL;
703
0
    while ( (element = hTable->nextElement(pos)) != NULL ) {
704
0
        const UHashTok valueTok = element->value;
705
0
        const UnicodeString* value = (UnicodeString*)valueTok.pointer;
706
0
        delete[] value;
707
0
    }
708
0
    delete fIntervalPatterns;
709
0
}
710
711
712
U_CDECL_BEGIN
713
714
/**
715
 * set hash table value comparator
716
 *
717
 * @param val1  one value in comparison
718
 * @param val2  the other value in comparison
719
 * @return      TRUE if 2 values are the same, FALSE otherwise
720
 */
721
static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
722
723
static UBool
724
0
U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
725
0
    const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
726
0
    const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
727
0
    UBool ret = TRUE;
728
0
    int8_t i;
729
0
    for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) {
730
0
        ret = (pattern1[i] == pattern2[i]);
731
0
    }
732
0
    return ret;
733
0
}
734
735
U_CDECL_END
736
737
738
Hashtable*
739
0
DateIntervalInfo::initHash(UErrorCode& status) {
740
0
    if ( U_FAILURE(status) ) {
741
0
        return NULL;
742
0
    }
743
0
    Hashtable* hTable;
744
0
    if ( (hTable = new Hashtable(FALSE, status)) == NULL ) {
745
0
        status = U_MEMORY_ALLOCATION_ERROR;
746
0
        return NULL;
747
0
    }
748
0
    if ( U_FAILURE(status) ) {
749
0
        delete hTable;
750
0
        return NULL;
751
0
    }
752
0
    hTable->setValueComparator(dtitvinfHashTableValueComparator);
753
0
    return hTable;
754
0
}
755
756
757
void
758
DateIntervalInfo::copyHash(const Hashtable* source,
759
                           Hashtable* target,
760
0
                           UErrorCode& status) {
761
0
    if ( U_FAILURE(status) ) {
762
0
        return;
763
0
    }
764
0
    int32_t pos = UHASH_FIRST;
765
0
    const UHashElement* element = NULL;
766
0
    if ( source ) {
767
0
        while ( (element = source->nextElement(pos)) != NULL ) {
768
0
            const UHashTok keyTok = element->key;
769
0
            const UnicodeString* key = (UnicodeString*)keyTok.pointer;
770
0
            const UHashTok valueTok = element->value;
771
0
            const UnicodeString* value = (UnicodeString*)valueTok.pointer;
772
0
            UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
773
0
            int8_t i;
774
0
            for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
775
0
                copy[i] = value[i];
776
0
            }
777
0
            target->put(UnicodeString(*key), copy, status);
778
0
            if ( U_FAILURE(status) ) {
779
0
                return;
780
0
            }
781
0
        }
782
0
    }
783
0
}
784
785
786
U_NAMESPACE_END
787
788
#endif