1 /*******************************************************************************
2 * Copyright (C) 2008, International Business Machines Corporation and
3 * others. All Rights Reserved.
4 *******************************************************************************
8 *******************************************************************************
11 #include "unicode/dtitvinf.h"
14 #if !UCONFIG_NO_FORMATTING
16 //FIXME: define it in compiler time
17 //#define DTITVINF_DEBUG 1
25 #include "unicode/msgfmt.h"
26 #include "dtitv_impl.h"
39 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
42 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo
)
44 static const char gCalendarTag
[]="calendar";
45 static const char gGregorianTag
[]="gregorian";
46 static const char gIntervalDateTimePatternTag
[]="intervalFormats";
47 static const char gFallbackPatternTag
[]="fallback";
50 static const UChar gFirstPattern
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
};
52 static const UChar gSecondPattern
[] = {LEFT_CURLY_BRACKET
, DIGIT_ONE
, RIGHT_CURLY_BRACKET
};
55 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};
59 DateIntervalInfo::DateIntervalInfo(UErrorCode
& status
)
60 : fFallbackIntervalPattern(gDefaultFallbackPattern
),
61 fFirstDateInPtnIsLaterDate(false),
62 fIntervalPatterns(NULL
)
64 fIntervalPatterns
= initHash(status
);
69 DateIntervalInfo::DateIntervalInfo(const Locale
& locale
, UErrorCode
& status
)
70 : fFallbackIntervalPattern(gDefaultFallbackPattern
),
71 fFirstDateInPtnIsLaterDate(false),
72 fIntervalPatterns(NULL
)
74 initializeData(locale
, status
);
80 DateIntervalInfo::setIntervalPattern(const UnicodeString
& skeleton
,
81 UCalendarDateFields lrgDiffCalUnit
,
82 const UnicodeString
& intervalPattern
,
85 if ( lrgDiffCalUnit
== UCAL_HOUR_OF_DAY
) {
86 setIntervalPatternInternally(skeleton
, UCAL_AM_PM
, intervalPattern
, status
);
87 setIntervalPatternInternally(skeleton
, UCAL_HOUR
, intervalPattern
, status
);
88 } else if ( lrgDiffCalUnit
== UCAL_DAY_OF_MONTH
||
89 lrgDiffCalUnit
== UCAL_DAY_OF_WEEK
) {
90 setIntervalPatternInternally(skeleton
, UCAL_DATE
, intervalPattern
, status
);
92 setIntervalPatternInternally(skeleton
, lrgDiffCalUnit
, intervalPattern
, status
);
98 DateIntervalInfo::setFallbackIntervalPattern(
99 const UnicodeString
& fallbackPattern
,
100 UErrorCode
& status
) {
101 if ( U_FAILURE(status
) ) {
104 int32_t firstPatternIndex
= fallbackPattern
.indexOf(gFirstPattern
,
105 sizeof(gFirstPattern
)/sizeof(gFirstPattern
[0]), 0);
106 int32_t secondPatternIndex
= fallbackPattern
.indexOf(gSecondPattern
,
107 sizeof(gSecondPattern
)/sizeof(gSecondPattern
[0]), 0);
108 if ( firstPatternIndex
== -1 || secondPatternIndex
== -1 ) {
109 status
= U_ILLEGAL_ARGUMENT_ERROR
;
112 if ( firstPatternIndex
> secondPatternIndex
) {
113 fFirstDateInPtnIsLaterDate
= true;
115 fFallbackIntervalPattern
= fallbackPattern
;
120 DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo
& dtitvinf
)
122 fIntervalPatterns(NULL
)
130 DateIntervalInfo::operator=(const DateIntervalInfo
& dtitvinf
) {
131 if ( this == &dtitvinf
) {
135 UErrorCode status
= U_ZERO_ERROR
;
136 deleteHash(fIntervalPatterns
);
137 fIntervalPatterns
= initHash(status
);
138 copyHash(dtitvinf
.fIntervalPatterns
, fIntervalPatterns
, status
);
139 if ( U_FAILURE(status
) ) {
143 fFallbackIntervalPattern
= dtitvinf
.fFallbackIntervalPattern
;
144 fFirstDateInPtnIsLaterDate
= dtitvinf
.fFirstDateInPtnIsLaterDate
;
150 DateIntervalInfo::clone() const {
151 return new DateIntervalInfo(*this);
155 DateIntervalInfo::~DateIntervalInfo() {
156 deleteHash(fIntervalPatterns
);
157 fIntervalPatterns
= NULL
;
162 DateIntervalInfo::operator==(const DateIntervalInfo
& other
) const {
164 fFallbackIntervalPattern
== other
.fFallbackIntervalPattern
&&
165 fFirstDateInPtnIsLaterDate
== other
.fFirstDateInPtnIsLaterDate
);
167 if ( equal
== TRUE
) {
168 equal
= fIntervalPatterns
->equals(*(other
.fIntervalPatterns
));
176 DateIntervalInfo::getIntervalPattern(const UnicodeString
& skeleton
,
177 UCalendarDateFields field
,
178 UnicodeString
& result
,
179 UErrorCode
& status
) const {
180 if ( U_FAILURE(status
) ) {
184 const UnicodeString
* patternsOfOneSkeleton
= (UnicodeString
*) fIntervalPatterns
->get(skeleton
);
185 if ( patternsOfOneSkeleton
!= NULL
) {
186 IntervalPatternIndex index
= calendarFieldToIntervalIndex(field
, status
);
187 if ( U_FAILURE(status
) ) {
190 const UnicodeString
& intervalPattern
= patternsOfOneSkeleton
[index
];
191 if ( !intervalPattern
.isEmpty() ) {
192 result
= intervalPattern
;
200 DateIntervalInfo::getDefaultOrder() const {
201 return fFirstDateInPtnIsLaterDate
;
206 DateIntervalInfo::getFallbackIntervalPattern(UnicodeString
& result
) const {
207 result
= fFallbackIntervalPattern
;
213 DateIntervalInfo::initializeData(const Locale
& locale
, UErrorCode
& err
)
215 fIntervalPatterns
= initHash(err
);
216 if ( U_FAILURE(err
) ) {
219 const char *locName
= locale
.getName();
220 char parentLocale
[ULOC_FULLNAME_CAPACITY
];
222 uprv_strcpy(parentLocale
, locName
);
223 UErrorCode status
= U_ZERO_ERROR
;
224 Hashtable
skeletonSet(TRUE
, status
);
225 if ( U_FAILURE(status
) ) {
229 UResourceBundle
*rb
, *calBundle
, *gregorianBundle
, *itvDtPtnResource
;
230 rb
= ures_open(NULL
, parentLocale
, &status
);
231 calBundle
= ures_getByKey(rb
, gCalendarTag
, NULL
, &status
);
232 gregorianBundle
= ures_getByKey(calBundle
, gGregorianTag
, NULL
, &status
);
233 itvDtPtnResource
= ures_getByKeyWithFallback(gregorianBundle
,
234 gIntervalDateTimePatternTag
, NULL
, &status
);
236 if ( U_SUCCESS(status
) ) {
237 // look for fallback first, since it establishes the default order
239 int32_t resStrLen
= 0;
240 resStr
= ures_getStringByKeyWithFallback(itvDtPtnResource
,
242 &resStrLen
, &status
);
243 if ( U_SUCCESS(status
) ) {
244 UnicodeString pattern
= UnicodeString(TRUE
, resStr
, resStrLen
);
245 setFallbackIntervalPattern(pattern
, status
);
248 int32_t size
= ures_getSize(itvDtPtnResource
);
250 for ( index
= 0; index
< size
; ++index
) {
251 UResourceBundle
* oneRes
= ures_getByIndex(itvDtPtnResource
, index
,
253 if ( U_SUCCESS(status
) ) {
254 const char* skeleton
= ures_getKey(oneRes
);
255 if ( skeleton
== NULL
||
256 skeletonSet
.geti(UnicodeString(skeleton
)) == 1 ) {
260 skeletonSet
.puti(UnicodeString(skeleton
), 1, status
);
261 if ( uprv_strcmp(skeleton
, gFallbackPatternTag
) == 0 ) {
263 continue; // fallback
266 UResourceBundle
* intervalPatterns
= ures_getByKey(
267 itvDtPtnResource
, skeleton
, NULL
, &status
);
269 if ( U_FAILURE(status
) ) {
270 ures_close(intervalPatterns
);
274 if ( intervalPatterns
== NULL
) {
275 ures_close(intervalPatterns
);
280 const UChar
* pattern
;
283 int32_t ptnNum
= ures_getSize(intervalPatterns
);
285 for ( ptnIndex
= 0; ptnIndex
< ptnNum
; ++ptnIndex
) {
286 pattern
= ures_getNextString(intervalPatterns
, &ptLength
, &key
,
288 if ( U_FAILURE(status
) ) {
292 UCalendarDateFields calendarField
= UCAL_FIELD_COUNT
;
293 if ( !uprv_strcmp(key
, "y") ) {
294 calendarField
= UCAL_YEAR
;
295 } else if ( !uprv_strcmp(key
, "M") ) {
296 calendarField
= UCAL_MONTH
;
297 } else if ( !uprv_strcmp(key
, "d") ) {
298 calendarField
= UCAL_DATE
;
299 } else if ( !uprv_strcmp(key
, "a") ) {
300 calendarField
= UCAL_AM_PM
;
301 } else if ( !uprv_strcmp(key
, "h") ) {
302 calendarField
= UCAL_HOUR
;
303 } else if ( !uprv_strcmp(key
, "m") ) {
304 calendarField
= UCAL_MINUTE
;
306 if ( calendarField
!= UCAL_FIELD_COUNT
) {
307 setIntervalPatternInternally(skeleton
, calendarField
, pattern
,status
);
310 ures_close(intervalPatterns
);
315 ures_close(itvDtPtnResource
);
316 ures_close(gregorianBundle
);
317 ures_close(calBundle
);
319 status
= U_ZERO_ERROR
;
320 locNameLen
= uloc_getParent(parentLocale
, parentLocale
,50,&status
);
321 } while ( locNameLen
> 0 );
327 DateIntervalInfo::setIntervalPatternInternally(const UnicodeString
& skeleton
,
328 UCalendarDateFields lrgDiffCalUnit
,
329 const UnicodeString
& intervalPattern
,
330 UErrorCode
& status
) {
331 IntervalPatternIndex index
= calendarFieldToIntervalIndex(lrgDiffCalUnit
,status
);
332 if ( U_FAILURE(status
) ) {
335 UnicodeString
* patternsOfOneSkeleton
= (UnicodeString
*)(fIntervalPatterns
->get(skeleton
));
336 UBool emptyHash
= false;
337 if ( patternsOfOneSkeleton
== NULL
) {
338 patternsOfOneSkeleton
= new UnicodeString
[kIPI_MAX_INDEX
];
342 patternsOfOneSkeleton
[index
] = intervalPattern
;
343 if ( emptyHash
== TRUE
) {
344 fIntervalPatterns
->put(skeleton
, patternsOfOneSkeleton
, status
);
351 DateIntervalInfo::parseSkeleton(const UnicodeString
& skeleton
,
352 int32_t* skeletonFieldWidth
) {
353 const int8_t PATTERN_CHAR_BASE
= 0x41;
355 for ( i
= 0; i
< skeleton
.length(); ++i
) {
356 // it is an ASCII char in skeleton
357 int8_t ch
= (int8_t)skeleton
.charAt(i
);
358 ++skeletonFieldWidth
[ch
- PATTERN_CHAR_BASE
];
365 DateIntervalInfo::stringNumeric(int32_t fieldWidth
, int32_t anotherFieldWidth
,
366 char patternLetter
) {
367 if ( patternLetter
== 'M' ) {
368 if ( fieldWidth
<= 2 && anotherFieldWidth
> 2 ||
369 fieldWidth
> 2 && anotherFieldWidth
<= 2 ) {
379 DateIntervalInfo::getBestSkeleton(const UnicodeString
& skeleton
,
380 int8_t& bestMatchDistanceInfo
) const {
381 #ifdef DTITVINF_DEBUG
385 skeleton
.extract(0, skeleton
.length(), result
, "UTF-8");
386 sprintf(mesg
, "in getBestSkeleton: skeleton: %s; \n", result
);
391 int32_t inputSkeletonFieldWidth
[] =
393 // A B C D E F G H I J K L M N O
394 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
395 // P Q R S T U V W X Y Z
396 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
397 // a b c d e f g h i j k l m n o
398 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
399 // p q r s t u v w x y z
400 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
403 int32_t skeletonFieldWidth
[] =
405 // A B C D E F G H I J K L M N O
406 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
407 // P Q R S T U V W X Y Z
408 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
409 // a b c d e f g h i j k l m n o
410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
411 // p q r s t u v w x y z
412 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
415 const int32_t DIFFERENT_FIELD
= 0x1000;
416 const int32_t STRING_NUMERIC_DIFFERENCE
= 0x100;
417 const int32_t BASE
= 0x41;
418 const UChar CHAR_V
= 0x0076;
419 const UChar CHAR_Z
= 0x007A;
421 // hack for 'v' and 'z'.
422 // resource bundle only have time skeletons ending with 'v',
423 // but not for time skeletons ending with 'z'.
424 UBool replaceZWithV
= false;
425 const UnicodeString
* inputSkeleton
= &skeleton
;
426 UnicodeString copySkeleton
;
427 if ( skeleton
.indexOf(CHAR_Z
) != -1 ) {
434 copySkeleton
= skeleton
;
435 copySkeleton
.findAndReplace(zstr
, vstr
);
436 inputSkeleton
= ©Skeleton
;
437 replaceZWithV
= true;
440 parseSkeleton(*inputSkeleton
, inputSkeletonFieldWidth
);
441 int32_t bestDistance
= MAX_POSITIVE_INT
;
442 const UnicodeString
* bestSkeleton
= NULL
;
444 // 0 means exact the same skeletons;
445 // 1 means having the same field, but with different length,
446 // 2 means only z/v differs
447 // -1 means having different field.
448 bestMatchDistanceInfo
= 0;
449 int8_t fieldLength
= sizeof(skeletonFieldWidth
)/sizeof(skeletonFieldWidth
[0]);
452 const UHashElement
* elem
= NULL
;
453 while ( (elem
= fIntervalPatterns
->nextElement(pos
)) != NULL
) {
454 const UHashTok keyTok
= elem
->key
;
455 UnicodeString
* skeleton
= (UnicodeString
*)keyTok
.pointer
;
456 #ifdef DTITVINF_DEBUG
457 skeleton
->extract(0, skeleton
->length(), result
, "UTF-8");
458 sprintf(mesg
, "available skeletons: skeleton: %s; \n", result
);
462 // clear skeleton field width
464 for ( i
= 0; i
< fieldLength
; ++i
) {
465 skeletonFieldWidth
[i
] = 0;
467 parseSkeleton(*skeleton
, skeletonFieldWidth
);
468 // calculate distance
469 int32_t distance
= 0;
470 int8_t fieldDifference
= 1;
471 for ( i
= 0; i
< fieldLength
; ++i
) {
472 int32_t inputFieldWidth
= inputSkeletonFieldWidth
[i
];
473 int32_t fieldWidth
= skeletonFieldWidth
[i
];
474 if ( inputFieldWidth
== fieldWidth
) {
477 if ( inputFieldWidth
== 0 ) {
478 fieldDifference
= -1;
479 distance
+= DIFFERENT_FIELD
;
480 } else if ( fieldWidth
== 0 ) {
481 fieldDifference
= -1;
482 distance
+= DIFFERENT_FIELD
;
483 } else if (stringNumeric(inputFieldWidth
, fieldWidth
,
485 distance
+= STRING_NUMERIC_DIFFERENCE
;
487 distance
+= (inputFieldWidth
> fieldWidth
) ?
488 (inputFieldWidth
- fieldWidth
) :
489 (fieldWidth
- inputFieldWidth
);
492 if ( distance
< bestDistance
) {
493 bestSkeleton
= skeleton
;
494 bestDistance
= distance
;
495 bestMatchDistanceInfo
= fieldDifference
;
497 if ( distance
== 0 ) {
498 bestMatchDistanceInfo
= 0;
502 if ( replaceZWithV
&& bestMatchDistanceInfo
!= -1 ) {
503 bestMatchDistanceInfo
= 2;
510 DateIntervalInfo::IntervalPatternIndex
511 DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field
,
512 UErrorCode
& status
) {
513 if ( U_FAILURE(status
) ) {
514 return kIPI_MAX_INDEX
;
516 IntervalPatternIndex index
= kIPI_MAX_INDEX
;
528 case UCAL_DAY_OF_WEEK
:
529 //case UCAL_DAY_OF_MONTH:
536 case UCAL_HOUR_OF_DAY
:
543 status
= U_ILLEGAL_ARGUMENT_ERROR
;
551 DateIntervalInfo::deleteHash(Hashtable
* hTable
)
553 if ( hTable
== NULL
) {
557 const UHashElement
* element
= NULL
;
558 while ( (element
= hTable
->nextElement(pos
)) != NULL
) {
559 const UHashTok keyTok
= element
->key
;
560 const UHashTok valueTok
= element
->value
;
561 const UnicodeString
* value
= (UnicodeString
*)valueTok
.pointer
;
564 delete fIntervalPatterns
;
571 * set hash table value comparator
573 * @param val1 one value in comparison
574 * @param val2 the other value in comparison
575 * @return TRUE if 2 values are the same, FALSE otherwise
577 static UBool U_CALLCONV
hashTableValueComparator(UHashTok val1
, UHashTok val2
);
582 U_CALLCONV
hashTableValueComparator(UHashTok val1
, UHashTok val2
) {
583 const UnicodeString
* pattern1
= (UnicodeString
*)val1
.pointer
;
584 const UnicodeString
* pattern2
= (UnicodeString
*)val2
.pointer
;
587 for ( i
= 0; i
< DateIntervalInfo::kIPI_MAX_INDEX
&& ret
== TRUE
; ++i
) {
588 ret
= (pattern1
[i
] == pattern2
[i
]);
596 DateIntervalInfo::initHash(UErrorCode
& status
) {
597 if ( U_FAILURE(status
) ) {
601 if ( (hTable
= new Hashtable(TRUE
, status
)) == NULL
) {
602 status
= U_MEMORY_ALLOCATION_ERROR
;
605 hTable
->setValueCompartor(hashTableValueComparator
);
611 DateIntervalInfo::copyHash(const Hashtable
* source
,
613 UErrorCode
& status
) {
614 if ( U_FAILURE(status
) ) {
618 const UHashElement
* element
= NULL
;
620 while ( (element
= source
->nextElement(pos
)) != NULL
) {
621 const UHashTok keyTok
= element
->key
;
622 const UnicodeString
* key
= (UnicodeString
*)keyTok
.pointer
;
623 const UHashTok valueTok
= element
->value
;
624 const UnicodeString
* value
= (UnicodeString
*)valueTok
.pointer
;
625 UnicodeString
* copy
= new UnicodeString
[kIPI_MAX_INDEX
];
627 for ( i
= 0; i
< kIPI_MAX_INDEX
; ++i
) {
630 target
->put(UnicodeString(*key
), copy
, status
);
631 if ( U_FAILURE(status
) ) {