Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /******************************************************************************* |
michael@0 | 2 | * Copyright (C) 2008-2012, International Business Machines Corporation and |
michael@0 | 3 | * others. All Rights Reserved. |
michael@0 | 4 | ******************************************************************************* |
michael@0 | 5 | * |
michael@0 | 6 | * File DTITVINF.CPP |
michael@0 | 7 | * |
michael@0 | 8 | ******************************************************************************* |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "unicode/dtitvinf.h" |
michael@0 | 12 | |
michael@0 | 13 | |
michael@0 | 14 | #if !UCONFIG_NO_FORMATTING |
michael@0 | 15 | |
michael@0 | 16 | //TODO: define it in compiler time |
michael@0 | 17 | //#define DTITVINF_DEBUG 1 |
michael@0 | 18 | |
michael@0 | 19 | |
michael@0 | 20 | #ifdef DTITVINF_DEBUG |
michael@0 | 21 | #include <iostream> |
michael@0 | 22 | #endif |
michael@0 | 23 | |
michael@0 | 24 | #include "cstring.h" |
michael@0 | 25 | #include "unicode/msgfmt.h" |
michael@0 | 26 | #include "unicode/uloc.h" |
michael@0 | 27 | #include "unicode/ures.h" |
michael@0 | 28 | #include "dtitv_impl.h" |
michael@0 | 29 | #include "hash.h" |
michael@0 | 30 | #include "gregoimp.h" |
michael@0 | 31 | #include "uresimp.h" |
michael@0 | 32 | #include "hash.h" |
michael@0 | 33 | #include "gregoimp.h" |
michael@0 | 34 | #include "uresimp.h" |
michael@0 | 35 | |
michael@0 | 36 | |
michael@0 | 37 | U_NAMESPACE_BEGIN |
michael@0 | 38 | |
michael@0 | 39 | |
michael@0 | 40 | #ifdef DTITVINF_DEBUG |
michael@0 | 41 | #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } |
michael@0 | 42 | #endif |
michael@0 | 43 | |
michael@0 | 44 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo) |
michael@0 | 45 | |
michael@0 | 46 | static const char gCalendarTag[]="calendar"; |
michael@0 | 47 | static const char gGregorianTag[]="gregorian"; |
michael@0 | 48 | static const char gIntervalDateTimePatternTag[]="intervalFormats"; |
michael@0 | 49 | static const char gFallbackPatternTag[]="fallback"; |
michael@0 | 50 | |
michael@0 | 51 | // {0} |
michael@0 | 52 | static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET}; |
michael@0 | 53 | // {1} |
michael@0 | 54 | static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET}; |
michael@0 | 55 | |
michael@0 | 56 | // default fall-back |
michael@0 | 57 | 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}; |
michael@0 | 58 | |
michael@0 | 59 | |
michael@0 | 60 | |
michael@0 | 61 | DateIntervalInfo::DateIntervalInfo(UErrorCode& status) |
michael@0 | 62 | : fFallbackIntervalPattern(gDefaultFallbackPattern), |
michael@0 | 63 | fFirstDateInPtnIsLaterDate(false), |
michael@0 | 64 | fIntervalPatterns(NULL) |
michael@0 | 65 | { |
michael@0 | 66 | fIntervalPatterns = initHash(status); |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | |
michael@0 | 70 | |
michael@0 | 71 | DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status) |
michael@0 | 72 | : fFallbackIntervalPattern(gDefaultFallbackPattern), |
michael@0 | 73 | fFirstDateInPtnIsLaterDate(false), |
michael@0 | 74 | fIntervalPatterns(NULL) |
michael@0 | 75 | { |
michael@0 | 76 | initializeData(locale, status); |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | |
michael@0 | 80 | |
michael@0 | 81 | void |
michael@0 | 82 | DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton, |
michael@0 | 83 | UCalendarDateFields lrgDiffCalUnit, |
michael@0 | 84 | const UnicodeString& intervalPattern, |
michael@0 | 85 | UErrorCode& status) { |
michael@0 | 86 | |
michael@0 | 87 | if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) { |
michael@0 | 88 | setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status); |
michael@0 | 89 | setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status); |
michael@0 | 90 | } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH || |
michael@0 | 91 | lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) { |
michael@0 | 92 | setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status); |
michael@0 | 93 | } else { |
michael@0 | 94 | setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status); |
michael@0 | 95 | } |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | |
michael@0 | 99 | void |
michael@0 | 100 | DateIntervalInfo::setFallbackIntervalPattern( |
michael@0 | 101 | const UnicodeString& fallbackPattern, |
michael@0 | 102 | UErrorCode& status) { |
michael@0 | 103 | if ( U_FAILURE(status) ) { |
michael@0 | 104 | return; |
michael@0 | 105 | } |
michael@0 | 106 | int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern, |
michael@0 | 107 | sizeof(gFirstPattern)/sizeof(gFirstPattern[0]), 0); |
michael@0 | 108 | int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern, |
michael@0 | 109 | sizeof(gSecondPattern)/sizeof(gSecondPattern[0]), 0); |
michael@0 | 110 | if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) { |
michael@0 | 111 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 112 | return; |
michael@0 | 113 | } |
michael@0 | 114 | if ( firstPatternIndex > secondPatternIndex ) { |
michael@0 | 115 | fFirstDateInPtnIsLaterDate = true; |
michael@0 | 116 | } |
michael@0 | 117 | fFallbackIntervalPattern = fallbackPattern; |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | |
michael@0 | 121 | |
michael@0 | 122 | DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf) |
michael@0 | 123 | : UObject(dtitvinf), |
michael@0 | 124 | fIntervalPatterns(NULL) |
michael@0 | 125 | { |
michael@0 | 126 | *this = dtitvinf; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | |
michael@0 | 130 | |
michael@0 | 131 | DateIntervalInfo& |
michael@0 | 132 | DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) { |
michael@0 | 133 | if ( this == &dtitvinf ) { |
michael@0 | 134 | return *this; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 138 | deleteHash(fIntervalPatterns); |
michael@0 | 139 | fIntervalPatterns = initHash(status); |
michael@0 | 140 | copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status); |
michael@0 | 141 | if ( U_FAILURE(status) ) { |
michael@0 | 142 | return *this; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern; |
michael@0 | 146 | fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate; |
michael@0 | 147 | return *this; |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | |
michael@0 | 151 | DateIntervalInfo* |
michael@0 | 152 | DateIntervalInfo::clone() const { |
michael@0 | 153 | return new DateIntervalInfo(*this); |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | |
michael@0 | 157 | DateIntervalInfo::~DateIntervalInfo() { |
michael@0 | 158 | deleteHash(fIntervalPatterns); |
michael@0 | 159 | fIntervalPatterns = NULL; |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | |
michael@0 | 163 | UBool |
michael@0 | 164 | DateIntervalInfo::operator==(const DateIntervalInfo& other) const { |
michael@0 | 165 | UBool equal = ( |
michael@0 | 166 | fFallbackIntervalPattern == other.fFallbackIntervalPattern && |
michael@0 | 167 | fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate ); |
michael@0 | 168 | |
michael@0 | 169 | if ( equal == TRUE ) { |
michael@0 | 170 | equal = fIntervalPatterns->equals(*(other.fIntervalPatterns)); |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | return equal; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | |
michael@0 | 177 | UnicodeString& |
michael@0 | 178 | DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton, |
michael@0 | 179 | UCalendarDateFields field, |
michael@0 | 180 | UnicodeString& result, |
michael@0 | 181 | UErrorCode& status) const { |
michael@0 | 182 | if ( U_FAILURE(status) ) { |
michael@0 | 183 | return result; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton); |
michael@0 | 187 | if ( patternsOfOneSkeleton != NULL ) { |
michael@0 | 188 | IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status); |
michael@0 | 189 | if ( U_FAILURE(status) ) { |
michael@0 | 190 | return result; |
michael@0 | 191 | } |
michael@0 | 192 | const UnicodeString& intervalPattern = patternsOfOneSkeleton[index]; |
michael@0 | 193 | if ( !intervalPattern.isEmpty() ) { |
michael@0 | 194 | result = intervalPattern; |
michael@0 | 195 | } |
michael@0 | 196 | } |
michael@0 | 197 | return result; |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | |
michael@0 | 201 | UBool |
michael@0 | 202 | DateIntervalInfo::getDefaultOrder() const { |
michael@0 | 203 | return fFirstDateInPtnIsLaterDate; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | |
michael@0 | 207 | UnicodeString& |
michael@0 | 208 | DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const { |
michael@0 | 209 | result = fFallbackIntervalPattern; |
michael@0 | 210 | return result; |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) |
michael@0 | 214 | |
michael@0 | 215 | void |
michael@0 | 216 | DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& err) |
michael@0 | 217 | { |
michael@0 | 218 | fIntervalPatterns = initHash(err); |
michael@0 | 219 | if ( U_FAILURE(err) ) { |
michael@0 | 220 | return; |
michael@0 | 221 | } |
michael@0 | 222 | const char *locName = locale.getName(); |
michael@0 | 223 | char parentLocale[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 224 | uprv_strcpy(parentLocale, locName); |
michael@0 | 225 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 226 | Hashtable skeletonSet(FALSE, status); |
michael@0 | 227 | if ( U_FAILURE(status) ) { |
michael@0 | 228 | return; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | // determine calendar type |
michael@0 | 232 | const char * calendarTypeToUse = gGregorianTag; // initial default |
michael@0 | 233 | char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well |
michael@0 | 234 | char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; |
michael@0 | 235 | // obtain a locale that always has the calendar key value that should be used |
michael@0 | 236 | (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL, |
michael@0 | 237 | "calendar", "calendar", locName, NULL, FALSE, &status); |
michael@0 | 238 | localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination |
michael@0 | 239 | // now get the calendar key value from that locale |
michael@0 | 240 | int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, ULOC_KEYWORDS_CAPACITY, &status); |
michael@0 | 241 | if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { |
michael@0 | 242 | calendarTypeToUse = calendarType; |
michael@0 | 243 | } |
michael@0 | 244 | status = U_ZERO_ERROR; |
michael@0 | 245 | |
michael@0 | 246 | do { |
michael@0 | 247 | UResourceBundle *rb, *calBundle, *calTypeBundle, *itvDtPtnResource; |
michael@0 | 248 | rb = ures_open(NULL, parentLocale, &status); |
michael@0 | 249 | if ( U_FAILURE(status) ) { |
michael@0 | 250 | break; |
michael@0 | 251 | } |
michael@0 | 252 | calBundle = ures_getByKey(rb, gCalendarTag, NULL, &status); |
michael@0 | 253 | calTypeBundle = ures_getByKey(calBundle, calendarTypeToUse, NULL, &status); |
michael@0 | 254 | itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle, |
michael@0 | 255 | gIntervalDateTimePatternTag, NULL, &status); |
michael@0 | 256 | |
michael@0 | 257 | if ( U_SUCCESS(status) ) { |
michael@0 | 258 | // look for fallback first, since it establishes the default order |
michael@0 | 259 | const UChar* resStr; |
michael@0 | 260 | int32_t resStrLen = 0; |
michael@0 | 261 | resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, |
michael@0 | 262 | gFallbackPatternTag, |
michael@0 | 263 | &resStrLen, &status); |
michael@0 | 264 | if ( U_SUCCESS(status) ) { |
michael@0 | 265 | UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen); |
michael@0 | 266 | setFallbackIntervalPattern(pattern, status); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | int32_t size = ures_getSize(itvDtPtnResource); |
michael@0 | 270 | int32_t index; |
michael@0 | 271 | for ( index = 0; index < size; ++index ) { |
michael@0 | 272 | LocalUResourceBundlePointer oneRes(ures_getByIndex(itvDtPtnResource, index, |
michael@0 | 273 | NULL, &status)); |
michael@0 | 274 | if ( U_SUCCESS(status) ) { |
michael@0 | 275 | const char* skeleton = ures_getKey(oneRes.getAlias()); |
michael@0 | 276 | if (skeleton == NULL) { |
michael@0 | 277 | continue; |
michael@0 | 278 | } |
michael@0 | 279 | UnicodeString skeletonUniStr(skeleton, -1, US_INV); |
michael@0 | 280 | if ( skeletonSet.geti(skeletonUniStr) == 1 ) { |
michael@0 | 281 | continue; |
michael@0 | 282 | } |
michael@0 | 283 | skeletonSet.puti(skeletonUniStr, 1, status); |
michael@0 | 284 | if ( uprv_strcmp(skeleton, gFallbackPatternTag) == 0 ) { |
michael@0 | 285 | continue; // fallback |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | LocalUResourceBundlePointer intervalPatterns(ures_getByKey( |
michael@0 | 289 | itvDtPtnResource, skeleton, NULL, &status)); |
michael@0 | 290 | |
michael@0 | 291 | if ( U_FAILURE(status) ) { |
michael@0 | 292 | break; |
michael@0 | 293 | } |
michael@0 | 294 | if ( intervalPatterns == NULL ) { |
michael@0 | 295 | continue; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | const char* key; |
michael@0 | 299 | int32_t ptnNum = ures_getSize(intervalPatterns.getAlias()); |
michael@0 | 300 | int32_t ptnIndex; |
michael@0 | 301 | for ( ptnIndex = 0; ptnIndex < ptnNum; ++ptnIndex ) { |
michael@0 | 302 | UnicodeString pattern = |
michael@0 | 303 | ures_getNextUnicodeString(intervalPatterns.getAlias(), &key, &status); |
michael@0 | 304 | if ( U_FAILURE(status) ) { |
michael@0 | 305 | break; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | UCalendarDateFields calendarField = UCAL_FIELD_COUNT; |
michael@0 | 309 | if ( !uprv_strcmp(key, "y") ) { |
michael@0 | 310 | calendarField = UCAL_YEAR; |
michael@0 | 311 | } else if ( !uprv_strcmp(key, "M") ) { |
michael@0 | 312 | calendarField = UCAL_MONTH; |
michael@0 | 313 | } else if ( !uprv_strcmp(key, "d") ) { |
michael@0 | 314 | calendarField = UCAL_DATE; |
michael@0 | 315 | } else if ( !uprv_strcmp(key, "a") ) { |
michael@0 | 316 | calendarField = UCAL_AM_PM; |
michael@0 | 317 | } else if ( !uprv_strcmp(key, "h") || !uprv_strcmp(key, "H") ) { |
michael@0 | 318 | calendarField = UCAL_HOUR; |
michael@0 | 319 | } else if ( !uprv_strcmp(key, "m") ) { |
michael@0 | 320 | calendarField = UCAL_MINUTE; |
michael@0 | 321 | } |
michael@0 | 322 | if ( calendarField != UCAL_FIELD_COUNT ) { |
michael@0 | 323 | setIntervalPatternInternally(skeletonUniStr, calendarField, pattern,status); |
michael@0 | 324 | } |
michael@0 | 325 | } |
michael@0 | 326 | } |
michael@0 | 327 | } |
michael@0 | 328 | } |
michael@0 | 329 | ures_close(itvDtPtnResource); |
michael@0 | 330 | ures_close(calTypeBundle); |
michael@0 | 331 | ures_close(calBundle); |
michael@0 | 332 | |
michael@0 | 333 | status = U_ZERO_ERROR; |
michael@0 | 334 | // Find the name of the appropriate parent locale (from %%Parent if present, else |
michael@0 | 335 | // uloc_getParent on the actual locale name) |
michael@0 | 336 | // (It would be nice to have a ures function that did this...) |
michael@0 | 337 | int32_t locNameLen; |
michael@0 | 338 | const UChar * parentUName = ures_getStringByKey(rb, "%%Parent", &locNameLen, &status); |
michael@0 | 339 | if (U_SUCCESS(status) && status != U_USING_FALLBACK_WARNING && locNameLen < ULOC_FULLNAME_CAPACITY) { |
michael@0 | 340 | u_UCharsToChars(parentUName, parentLocale, locNameLen + 1); |
michael@0 | 341 | } else { |
michael@0 | 342 | status = U_ZERO_ERROR; |
michael@0 | 343 | // Get the actual name of the current locale being used |
michael@0 | 344 | const char *curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &status); |
michael@0 | 345 | if ( U_FAILURE(status) ) { |
michael@0 | 346 | curLocaleName = parentLocale; |
michael@0 | 347 | status = U_ZERO_ERROR; |
michael@0 | 348 | } |
michael@0 | 349 | uloc_getParent(curLocaleName, parentLocale, ULOC_FULLNAME_CAPACITY, &status); |
michael@0 | 350 | if (U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { |
michael@0 | 351 | parentLocale[0] = 0; // just fallback to root, will cause us to stop |
michael@0 | 352 | status = U_ZERO_ERROR; |
michael@0 | 353 | } |
michael@0 | 354 | } |
michael@0 | 355 | // Now we can close the current locale bundle |
michael@0 | 356 | ures_close(rb); |
michael@0 | 357 | // If the new current locale is root, then stop |
michael@0 | 358 | // (unlike for DateTimePatternGenerator, DateIntervalFormat does not go all the way up |
michael@0 | 359 | // to root to find additional data for non-root locales) |
michael@0 | 360 | } while ( parentLocale[0] != 0 && uprv_strcmp(parentLocale,"root")!=0 ); |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | |
michael@0 | 364 | |
michael@0 | 365 | void |
michael@0 | 366 | DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton, |
michael@0 | 367 | UCalendarDateFields lrgDiffCalUnit, |
michael@0 | 368 | const UnicodeString& intervalPattern, |
michael@0 | 369 | UErrorCode& status) { |
michael@0 | 370 | IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status); |
michael@0 | 371 | if ( U_FAILURE(status) ) { |
michael@0 | 372 | return; |
michael@0 | 373 | } |
michael@0 | 374 | UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton)); |
michael@0 | 375 | UBool emptyHash = false; |
michael@0 | 376 | if ( patternsOfOneSkeleton == NULL ) { |
michael@0 | 377 | patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX]; |
michael@0 | 378 | emptyHash = true; |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | patternsOfOneSkeleton[index] = intervalPattern; |
michael@0 | 382 | if ( emptyHash == TRUE ) { |
michael@0 | 383 | fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status); |
michael@0 | 384 | } |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | |
michael@0 | 388 | |
michael@0 | 389 | void |
michael@0 | 390 | DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton, |
michael@0 | 391 | int32_t* skeletonFieldWidth) { |
michael@0 | 392 | const int8_t PATTERN_CHAR_BASE = 0x41; |
michael@0 | 393 | int32_t i; |
michael@0 | 394 | for ( i = 0; i < skeleton.length(); ++i ) { |
michael@0 | 395 | // it is an ASCII char in skeleton |
michael@0 | 396 | int8_t ch = (int8_t)skeleton.charAt(i); |
michael@0 | 397 | ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE]; |
michael@0 | 398 | } |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | |
michael@0 | 402 | |
michael@0 | 403 | UBool |
michael@0 | 404 | DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth, |
michael@0 | 405 | char patternLetter) { |
michael@0 | 406 | if ( patternLetter == 'M' ) { |
michael@0 | 407 | if ( (fieldWidth <= 2 && anotherFieldWidth > 2) || |
michael@0 | 408 | (fieldWidth > 2 && anotherFieldWidth <= 2 )) { |
michael@0 | 409 | return true; |
michael@0 | 410 | } |
michael@0 | 411 | } |
michael@0 | 412 | return false; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | |
michael@0 | 416 | |
michael@0 | 417 | const UnicodeString* |
michael@0 | 418 | DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton, |
michael@0 | 419 | int8_t& bestMatchDistanceInfo) const { |
michael@0 | 420 | #ifdef DTITVINF_DEBUG |
michael@0 | 421 | char result[1000]; |
michael@0 | 422 | char result_1[1000]; |
michael@0 | 423 | char mesg[2000]; |
michael@0 | 424 | skeleton.extract(0, skeleton.length(), result, "UTF-8"); |
michael@0 | 425 | sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result); |
michael@0 | 426 | PRINTMESG(mesg) |
michael@0 | 427 | #endif |
michael@0 | 428 | |
michael@0 | 429 | |
michael@0 | 430 | int32_t inputSkeletonFieldWidth[] = |
michael@0 | 431 | { |
michael@0 | 432 | // A B C D E F G H I J K L M N O |
michael@0 | 433 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
michael@0 | 434 | // P Q R S T U V W X Y Z |
michael@0 | 435 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
michael@0 | 436 | // a b c d e f g h i j k l m n o |
michael@0 | 437 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
michael@0 | 438 | // p q r s t u v w x y z |
michael@0 | 439 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
michael@0 | 440 | }; |
michael@0 | 441 | |
michael@0 | 442 | int32_t skeletonFieldWidth[] = |
michael@0 | 443 | { |
michael@0 | 444 | // A B C D E F G H I J K L M N O |
michael@0 | 445 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
michael@0 | 446 | // P Q R S T U V W X Y Z |
michael@0 | 447 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
michael@0 | 448 | // a b c d e f g h i j k l m n o |
michael@0 | 449 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
michael@0 | 450 | // p q r s t u v w x y z |
michael@0 | 451 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
michael@0 | 452 | }; |
michael@0 | 453 | |
michael@0 | 454 | const int32_t DIFFERENT_FIELD = 0x1000; |
michael@0 | 455 | const int32_t STRING_NUMERIC_DIFFERENCE = 0x100; |
michael@0 | 456 | const int32_t BASE = 0x41; |
michael@0 | 457 | const UChar CHAR_V = 0x0076; |
michael@0 | 458 | const UChar CHAR_Z = 0x007A; |
michael@0 | 459 | |
michael@0 | 460 | // hack for 'v' and 'z'. |
michael@0 | 461 | // resource bundle only have time skeletons ending with 'v', |
michael@0 | 462 | // but not for time skeletons ending with 'z'. |
michael@0 | 463 | UBool replaceZWithV = false; |
michael@0 | 464 | const UnicodeString* inputSkeleton = &skeleton; |
michael@0 | 465 | UnicodeString copySkeleton; |
michael@0 | 466 | if ( skeleton.indexOf(CHAR_Z) != -1 ) { |
michael@0 | 467 | copySkeleton = skeleton; |
michael@0 | 468 | copySkeleton.findAndReplace(UnicodeString(CHAR_Z), UnicodeString(CHAR_V)); |
michael@0 | 469 | inputSkeleton = ©Skeleton; |
michael@0 | 470 | replaceZWithV = true; |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth); |
michael@0 | 474 | int32_t bestDistance = MAX_POSITIVE_INT; |
michael@0 | 475 | const UnicodeString* bestSkeleton = NULL; |
michael@0 | 476 | |
michael@0 | 477 | // 0 means exact the same skeletons; |
michael@0 | 478 | // 1 means having the same field, but with different length, |
michael@0 | 479 | // 2 means only z/v differs |
michael@0 | 480 | // -1 means having different field. |
michael@0 | 481 | bestMatchDistanceInfo = 0; |
michael@0 | 482 | int8_t fieldLength = sizeof(skeletonFieldWidth)/sizeof(skeletonFieldWidth[0]); |
michael@0 | 483 | |
michael@0 | 484 | int32_t pos = -1; |
michael@0 | 485 | const UHashElement* elem = NULL; |
michael@0 | 486 | while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) { |
michael@0 | 487 | const UHashTok keyTok = elem->key; |
michael@0 | 488 | UnicodeString* skeleton = (UnicodeString*)keyTok.pointer; |
michael@0 | 489 | #ifdef DTITVINF_DEBUG |
michael@0 | 490 | skeleton->extract(0, skeleton->length(), result, "UTF-8"); |
michael@0 | 491 | sprintf(mesg, "available skeletons: skeleton: %s; \n", result); |
michael@0 | 492 | PRINTMESG(mesg) |
michael@0 | 493 | #endif |
michael@0 | 494 | |
michael@0 | 495 | // clear skeleton field width |
michael@0 | 496 | int8_t i; |
michael@0 | 497 | for ( i = 0; i < fieldLength; ++i ) { |
michael@0 | 498 | skeletonFieldWidth[i] = 0; |
michael@0 | 499 | } |
michael@0 | 500 | parseSkeleton(*skeleton, skeletonFieldWidth); |
michael@0 | 501 | // calculate distance |
michael@0 | 502 | int32_t distance = 0; |
michael@0 | 503 | int8_t fieldDifference = 1; |
michael@0 | 504 | for ( i = 0; i < fieldLength; ++i ) { |
michael@0 | 505 | int32_t inputFieldWidth = inputSkeletonFieldWidth[i]; |
michael@0 | 506 | int32_t fieldWidth = skeletonFieldWidth[i]; |
michael@0 | 507 | if ( inputFieldWidth == fieldWidth ) { |
michael@0 | 508 | continue; |
michael@0 | 509 | } |
michael@0 | 510 | if ( inputFieldWidth == 0 ) { |
michael@0 | 511 | fieldDifference = -1; |
michael@0 | 512 | distance += DIFFERENT_FIELD; |
michael@0 | 513 | } else if ( fieldWidth == 0 ) { |
michael@0 | 514 | fieldDifference = -1; |
michael@0 | 515 | distance += DIFFERENT_FIELD; |
michael@0 | 516 | } else if (stringNumeric(inputFieldWidth, fieldWidth, |
michael@0 | 517 | (char)(i+BASE) ) ) { |
michael@0 | 518 | distance += STRING_NUMERIC_DIFFERENCE; |
michael@0 | 519 | } else { |
michael@0 | 520 | distance += (inputFieldWidth > fieldWidth) ? |
michael@0 | 521 | (inputFieldWidth - fieldWidth) : |
michael@0 | 522 | (fieldWidth - inputFieldWidth); |
michael@0 | 523 | } |
michael@0 | 524 | } |
michael@0 | 525 | if ( distance < bestDistance ) { |
michael@0 | 526 | bestSkeleton = skeleton; |
michael@0 | 527 | bestDistance = distance; |
michael@0 | 528 | bestMatchDistanceInfo = fieldDifference; |
michael@0 | 529 | } |
michael@0 | 530 | if ( distance == 0 ) { |
michael@0 | 531 | bestMatchDistanceInfo = 0; |
michael@0 | 532 | break; |
michael@0 | 533 | } |
michael@0 | 534 | } |
michael@0 | 535 | if ( replaceZWithV && bestMatchDistanceInfo != -1 ) { |
michael@0 | 536 | bestMatchDistanceInfo = 2; |
michael@0 | 537 | } |
michael@0 | 538 | return bestSkeleton; |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | |
michael@0 | 542 | |
michael@0 | 543 | DateIntervalInfo::IntervalPatternIndex |
michael@0 | 544 | DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field, |
michael@0 | 545 | UErrorCode& status) { |
michael@0 | 546 | if ( U_FAILURE(status) ) { |
michael@0 | 547 | return kIPI_MAX_INDEX; |
michael@0 | 548 | } |
michael@0 | 549 | IntervalPatternIndex index = kIPI_MAX_INDEX; |
michael@0 | 550 | switch ( field ) { |
michael@0 | 551 | case UCAL_ERA: |
michael@0 | 552 | index = kIPI_ERA; |
michael@0 | 553 | break; |
michael@0 | 554 | case UCAL_YEAR: |
michael@0 | 555 | index = kIPI_YEAR; |
michael@0 | 556 | break; |
michael@0 | 557 | case UCAL_MONTH: |
michael@0 | 558 | index = kIPI_MONTH; |
michael@0 | 559 | break; |
michael@0 | 560 | case UCAL_DATE: |
michael@0 | 561 | case UCAL_DAY_OF_WEEK: |
michael@0 | 562 | //case UCAL_DAY_OF_MONTH: |
michael@0 | 563 | index = kIPI_DATE; |
michael@0 | 564 | break; |
michael@0 | 565 | case UCAL_AM_PM: |
michael@0 | 566 | index = kIPI_AM_PM; |
michael@0 | 567 | break; |
michael@0 | 568 | case UCAL_HOUR: |
michael@0 | 569 | case UCAL_HOUR_OF_DAY: |
michael@0 | 570 | index = kIPI_HOUR; |
michael@0 | 571 | break; |
michael@0 | 572 | case UCAL_MINUTE: |
michael@0 | 573 | index = kIPI_MINUTE; |
michael@0 | 574 | break; |
michael@0 | 575 | default: |
michael@0 | 576 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 577 | } |
michael@0 | 578 | return index; |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | |
michael@0 | 582 | |
michael@0 | 583 | void |
michael@0 | 584 | DateIntervalInfo::deleteHash(Hashtable* hTable) |
michael@0 | 585 | { |
michael@0 | 586 | if ( hTable == NULL ) { |
michael@0 | 587 | return; |
michael@0 | 588 | } |
michael@0 | 589 | int32_t pos = -1; |
michael@0 | 590 | const UHashElement* element = NULL; |
michael@0 | 591 | while ( (element = hTable->nextElement(pos)) != NULL ) { |
michael@0 | 592 | const UHashTok valueTok = element->value; |
michael@0 | 593 | const UnicodeString* value = (UnicodeString*)valueTok.pointer; |
michael@0 | 594 | delete[] value; |
michael@0 | 595 | } |
michael@0 | 596 | delete fIntervalPatterns; |
michael@0 | 597 | } |
michael@0 | 598 | |
michael@0 | 599 | |
michael@0 | 600 | U_CDECL_BEGIN |
michael@0 | 601 | |
michael@0 | 602 | /** |
michael@0 | 603 | * set hash table value comparator |
michael@0 | 604 | * |
michael@0 | 605 | * @param val1 one value in comparison |
michael@0 | 606 | * @param val2 the other value in comparison |
michael@0 | 607 | * @return TRUE if 2 values are the same, FALSE otherwise |
michael@0 | 608 | */ |
michael@0 | 609 | static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2); |
michael@0 | 610 | |
michael@0 | 611 | static UBool |
michael@0 | 612 | U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) { |
michael@0 | 613 | const UnicodeString* pattern1 = (UnicodeString*)val1.pointer; |
michael@0 | 614 | const UnicodeString* pattern2 = (UnicodeString*)val2.pointer; |
michael@0 | 615 | UBool ret = TRUE; |
michael@0 | 616 | int8_t i; |
michael@0 | 617 | for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) { |
michael@0 | 618 | ret = (pattern1[i] == pattern2[i]); |
michael@0 | 619 | } |
michael@0 | 620 | return ret; |
michael@0 | 621 | } |
michael@0 | 622 | |
michael@0 | 623 | U_CDECL_END |
michael@0 | 624 | |
michael@0 | 625 | |
michael@0 | 626 | Hashtable* |
michael@0 | 627 | DateIntervalInfo::initHash(UErrorCode& status) { |
michael@0 | 628 | if ( U_FAILURE(status) ) { |
michael@0 | 629 | return NULL; |
michael@0 | 630 | } |
michael@0 | 631 | Hashtable* hTable; |
michael@0 | 632 | if ( (hTable = new Hashtable(FALSE, status)) == NULL ) { |
michael@0 | 633 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 634 | return NULL; |
michael@0 | 635 | } |
michael@0 | 636 | if ( U_FAILURE(status) ) { |
michael@0 | 637 | delete hTable; |
michael@0 | 638 | return NULL; |
michael@0 | 639 | } |
michael@0 | 640 | hTable->setValueComparator(dtitvinfHashTableValueComparator); |
michael@0 | 641 | return hTable; |
michael@0 | 642 | } |
michael@0 | 643 | |
michael@0 | 644 | |
michael@0 | 645 | void |
michael@0 | 646 | DateIntervalInfo::copyHash(const Hashtable* source, |
michael@0 | 647 | Hashtable* target, |
michael@0 | 648 | UErrorCode& status) { |
michael@0 | 649 | if ( U_FAILURE(status) ) { |
michael@0 | 650 | return; |
michael@0 | 651 | } |
michael@0 | 652 | int32_t pos = -1; |
michael@0 | 653 | const UHashElement* element = NULL; |
michael@0 | 654 | if ( source ) { |
michael@0 | 655 | while ( (element = source->nextElement(pos)) != NULL ) { |
michael@0 | 656 | const UHashTok keyTok = element->key; |
michael@0 | 657 | const UnicodeString* key = (UnicodeString*)keyTok.pointer; |
michael@0 | 658 | const UHashTok valueTok = element->value; |
michael@0 | 659 | const UnicodeString* value = (UnicodeString*)valueTok.pointer; |
michael@0 | 660 | UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX]; |
michael@0 | 661 | int8_t i; |
michael@0 | 662 | for ( i = 0; i < kIPI_MAX_INDEX; ++i ) { |
michael@0 | 663 | copy[i] = value[i]; |
michael@0 | 664 | } |
michael@0 | 665 | target->put(UnicodeString(*key), copy, status); |
michael@0 | 666 | if ( U_FAILURE(status) ) { |
michael@0 | 667 | return; |
michael@0 | 668 | } |
michael@0 | 669 | } |
michael@0 | 670 | } |
michael@0 | 671 | } |
michael@0 | 672 | |
michael@0 | 673 | |
michael@0 | 674 | U_NAMESPACE_END |
michael@0 | 675 | |
michael@0 | 676 | #endif |