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