tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dtitvinf.cpp (28254B)


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