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