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 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 = ©Skeleton; 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