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