Home | History | Annotate | Download | only in i18n
      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 {
     68     fIntervalPatterns = initHash(status);
     69 }
     70 
     71 
     72 
     73 DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
     74 :   fFallbackIntervalPattern(gDefaultFallbackPattern),
     75     fFirstDateInPtnIsLaterDate(false),
     76     fIntervalPatterns(NULL)
     77 {
     78     initializeData(locale, status);
     79 }
     80 
     81 
     82 
     83 void
     84 DateIntervalInfo::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 
    101 void
    102 DateIntervalInfo::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 
    124 DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
    125 :   UObject(dtitvinf),
    126     fIntervalPatterns(NULL)
    127 {
    128     *this = dtitvinf;
    129 }
    130 
    131 
    132 
    133 DateIntervalInfo&
    134 DateIntervalInfo::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 
    153 DateIntervalInfo*
    154 DateIntervalInfo::clone() const {
    155     return new DateIntervalInfo(*this);
    156 }
    157 
    158 
    159 DateIntervalInfo::~DateIntervalInfo() {
    160     deleteHash(fIntervalPatterns);
    161     fIntervalPatterns = NULL;
    162 }
    163 
    164 
    165 UBool
    166 DateIntervalInfo::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 
    179 UnicodeString&
    180 DateIntervalInfo::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 
    203 UBool
    204 DateIntervalInfo::getDefaultOrder() const {
    205     return fFirstDateInPtnIsLaterDate;
    206 }
    207 
    208 
    209 UnicodeString&
    210 DateIntervalInfo::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 
    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             : 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.
    378 DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
    379 
    380 
    381 
    382 void
    383 DateIntervalInfo::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 
    474 void
    475 DateIntervalInfo::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 
    498 void
    499 DateIntervalInfo::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 
    512 UBool
    513 DateIntervalInfo::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 
    526 const UnicodeString*
    527 DateIntervalInfo::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 
    652 DateIntervalInfo::IntervalPatternIndex
    653 DateIntervalInfo::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 
    695 void
    696 DateIntervalInfo::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 
    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 U_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 
    735 U_CDECL_END
    736 
    737 
    738 Hashtable*
    739 DateIntervalInfo::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 
    757 void
    758 DateIntervalInfo::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 
    786 U_NAMESPACE_END
    787 
    788 #endif
    789