]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/dtitvinf.cpp
ICU-400.42.tar.gz
[apple/icu.git] / icuSources / i18n / dtitvinf.cpp
1 /*******************************************************************************
2 * Copyright (C) 2008, International Business Machines Corporation and
3 * others. All Rights Reserved.
4 *******************************************************************************
5 *
6 * File DTITVINF.CPP
7 *
8 *******************************************************************************
9 */
10
11 #include "unicode/dtitvinf.h"
12
13
14 #if !UCONFIG_NO_FORMATTING
15
16 //FIXME: define it in compiler time
17 //#define DTITVINF_DEBUG 1
18
19
20 #ifdef DTITVINF_DEBUG
21 #include <iostream>
22 #endif
23
24 #include "cstring.h"
25 #include "unicode/msgfmt.h"
26 #include "dtitv_impl.h"
27 #include "hash.h"
28 #include "gregoimp.h"
29 #include "uresimp.h"
30 #include "hash.h"
31 #include "gregoimp.h"
32 #include "uresimp.h"
33
34
35 U_NAMESPACE_BEGIN
36
37
38 #ifdef DTITVINF_DEBUG
39 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
40 #endif
41
42 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
43
44 static const char gCalendarTag[]="calendar";
45 static const char gGregorianTag[]="gregorian";
46 static const char gIntervalDateTimePatternTag[]="intervalFormats";
47 static const char gFallbackPatternTag[]="fallback";
48
49 // {0}
50 static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
51 // {1}
52 static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
53
54 // default fall-back
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};
56
57
58
59 DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
60 : fFallbackIntervalPattern(gDefaultFallbackPattern),
61 fFirstDateInPtnIsLaterDate(false),
62 fIntervalPatterns(NULL)
63 {
64 fIntervalPatterns = initHash(status);
65 }
66
67
68
69 DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
70 : fFallbackIntervalPattern(gDefaultFallbackPattern),
71 fFirstDateInPtnIsLaterDate(false),
72 fIntervalPatterns(NULL)
73 {
74 initializeData(locale, status);
75 }
76
77
78
79 void
80 DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
81 UCalendarDateFields lrgDiffCalUnit,
82 const UnicodeString& intervalPattern,
83 UErrorCode& status) {
84
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);
91 } else {
92 setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
93 }
94 }
95
96
97 void
98 DateIntervalInfo::setFallbackIntervalPattern(
99 const UnicodeString& fallbackPattern,
100 UErrorCode& status) {
101 if ( U_FAILURE(status) ) {
102 return;
103 }
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;
110 return;
111 }
112 if ( firstPatternIndex > secondPatternIndex ) {
113 fFirstDateInPtnIsLaterDate = true;
114 }
115 fFallbackIntervalPattern = fallbackPattern;
116 }
117
118
119
120 DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
121 : UObject(dtitvinf),
122 fIntervalPatterns(NULL)
123 {
124 *this = dtitvinf;
125 }
126
127
128
129 DateIntervalInfo&
130 DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
131 if ( this == &dtitvinf ) {
132 return *this;
133 }
134
135 UErrorCode status = U_ZERO_ERROR;
136 deleteHash(fIntervalPatterns);
137 fIntervalPatterns = initHash(status);
138 copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
139 if ( U_FAILURE(status) ) {
140 return *this;
141 }
142
143 fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
144 fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
145 return *this;
146 }
147
148
149 DateIntervalInfo*
150 DateIntervalInfo::clone() const {
151 return new DateIntervalInfo(*this);
152 }
153
154
155 DateIntervalInfo::~DateIntervalInfo() {
156 deleteHash(fIntervalPatterns);
157 fIntervalPatterns = NULL;
158 }
159
160
161 UBool
162 DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
163 UBool equal = (
164 fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
165 fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
166
167 if ( equal == TRUE ) {
168 equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
169 }
170
171 return equal;
172 }
173
174
175 UnicodeString&
176 DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
177 UCalendarDateFields field,
178 UnicodeString& result,
179 UErrorCode& status) const {
180 if ( U_FAILURE(status) ) {
181 return result;
182 }
183
184 const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
185 if ( patternsOfOneSkeleton != NULL ) {
186 IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
187 if ( U_FAILURE(status) ) {
188 return result;
189 }
190 const UnicodeString& intervalPattern = patternsOfOneSkeleton[index];
191 if ( !intervalPattern.isEmpty() ) {
192 result = intervalPattern;
193 }
194 }
195 return result;
196 }
197
198
199 UBool
200 DateIntervalInfo::getDefaultOrder() const {
201 return fFirstDateInPtnIsLaterDate;
202 }
203
204
205 UnicodeString&
206 DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
207 result = fFallbackIntervalPattern;
208 return result;
209 }
210
211
212 void
213 DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& err)
214 {
215 fIntervalPatterns = initHash(err);
216 if ( U_FAILURE(err) ) {
217 return;
218 }
219 const char *locName = locale.getName();
220 char parentLocale[ULOC_FULLNAME_CAPACITY];
221 int32_t locNameLen;
222 uprv_strcpy(parentLocale, locName);
223 UErrorCode status = U_ZERO_ERROR;
224 Hashtable skeletonSet(TRUE, status);
225 if ( U_FAILURE(status) ) {
226 return;
227 }
228 do {
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);
235
236 if ( U_SUCCESS(status) ) {
237 // look for fallback first, since it establishes the default order
238 const UChar* resStr;
239 int32_t resStrLen = 0;
240 resStr = ures_getStringByKeyWithFallback(itvDtPtnResource,
241 gFallbackPatternTag,
242 &resStrLen, &status);
243 if ( U_SUCCESS(status) ) {
244 UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
245 setFallbackIntervalPattern(pattern, status);
246 }
247
248 int32_t size = ures_getSize(itvDtPtnResource);
249 int32_t index;
250 for ( index = 0; index < size; ++index ) {
251 UResourceBundle* oneRes = ures_getByIndex(itvDtPtnResource, index,
252 NULL, &status);
253 if ( U_SUCCESS(status) ) {
254 const char* skeleton = ures_getKey(oneRes);
255 if ( skeleton == NULL ||
256 skeletonSet.geti(UnicodeString(skeleton)) == 1 ) {
257 ures_close(oneRes);
258 continue;
259 }
260 skeletonSet.puti(UnicodeString(skeleton), 1, status);
261 if ( uprv_strcmp(skeleton, gFallbackPatternTag) == 0 ) {
262 ures_close(oneRes);
263 continue; // fallback
264 }
265
266 UResourceBundle* intervalPatterns = ures_getByKey(
267 itvDtPtnResource, skeleton, NULL, &status);
268
269 if ( U_FAILURE(status) ) {
270 ures_close(intervalPatterns);
271 ures_close(oneRes);
272 break;
273 }
274 if ( intervalPatterns == NULL ) {
275 ures_close(intervalPatterns);
276 ures_close(oneRes);
277 continue;
278 }
279
280 const UChar* pattern;
281 const char* key;
282 int32_t ptLength;
283 int32_t ptnNum = ures_getSize(intervalPatterns);
284 int32_t ptnIndex;
285 for ( ptnIndex = 0; ptnIndex < ptnNum; ++ptnIndex ) {
286 pattern = ures_getNextString(intervalPatterns, &ptLength, &key,
287 &status);
288 if ( U_FAILURE(status) ) {
289 break;
290 }
291
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;
305 }
306 if ( calendarField != UCAL_FIELD_COUNT ) {
307 setIntervalPatternInternally(skeleton, calendarField, pattern,status);
308 }
309 }
310 ures_close(intervalPatterns);
311 }
312 ures_close(oneRes);
313 }
314 }
315 ures_close(itvDtPtnResource);
316 ures_close(gregorianBundle);
317 ures_close(calBundle);
318 ures_close(rb);
319 status = U_ZERO_ERROR;
320 locNameLen = uloc_getParent(parentLocale, parentLocale,50,&status);
321 } while ( locNameLen > 0 );
322 }
323
324
325
326 void
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) ) {
333 return;
334 }
335 UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
336 UBool emptyHash = false;
337 if ( patternsOfOneSkeleton == NULL ) {
338 patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
339 emptyHash = true;
340 }
341
342 patternsOfOneSkeleton[index] = intervalPattern;
343 if ( emptyHash == TRUE ) {
344 fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
345 }
346 }
347
348
349
350 void
351 DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
352 int32_t* skeletonFieldWidth) {
353 const int8_t PATTERN_CHAR_BASE = 0x41;
354 int32_t i;
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];
359 }
360 }
361
362
363
364 UBool
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 ) {
370 return true;
371 }
372 }
373 return false;
374 }
375
376
377
378 const UnicodeString*
379 DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
380 int8_t& bestMatchDistanceInfo) const {
381 #ifdef DTITVINF_DEBUG
382 char result[1000];
383 char result_1[1000];
384 char mesg[2000];
385 skeleton.extract(0, skeleton.length(), result, "UTF-8");
386 sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result);
387 PRINTMESG(mesg)
388 #endif
389
390
391 int32_t inputSkeletonFieldWidth[] =
392 {
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
401 };
402
403 int32_t skeletonFieldWidth[] =
404 {
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
413 };
414
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;
420
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 ) {
428 UChar zstr[2];
429 UChar vstr[2];
430 zstr[0]=CHAR_Z;
431 vstr[0]=CHAR_V;
432 zstr[1]=0;
433 vstr[1]=0;
434 copySkeleton = skeleton;
435 copySkeleton.findAndReplace(zstr, vstr);
436 inputSkeleton = &copySkeleton;
437 replaceZWithV = true;
438 }
439
440 parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
441 int32_t bestDistance = MAX_POSITIVE_INT;
442 const UnicodeString* bestSkeleton = NULL;
443
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]);
450
451 int32_t pos = -1;
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);
459 PRINTMESG(mesg)
460 #endif
461
462 // clear skeleton field width
463 int8_t i;
464 for ( i = 0; i < fieldLength; ++i ) {
465 skeletonFieldWidth[i] = 0;
466 }
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 ) {
475 continue;
476 }
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,
484 (char)(i+BASE) ) ) {
485 distance += STRING_NUMERIC_DIFFERENCE;
486 } else {
487 distance += (inputFieldWidth > fieldWidth) ?
488 (inputFieldWidth - fieldWidth) :
489 (fieldWidth - inputFieldWidth);
490 }
491 }
492 if ( distance < bestDistance ) {
493 bestSkeleton = skeleton;
494 bestDistance = distance;
495 bestMatchDistanceInfo = fieldDifference;
496 }
497 if ( distance == 0 ) {
498 bestMatchDistanceInfo = 0;
499 break;
500 }
501 }
502 if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
503 bestMatchDistanceInfo = 2;
504 }
505 return bestSkeleton;
506 }
507
508
509
510 DateIntervalInfo::IntervalPatternIndex
511 DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
512 UErrorCode& status) {
513 if ( U_FAILURE(status) ) {
514 return kIPI_MAX_INDEX;
515 }
516 IntervalPatternIndex index = kIPI_MAX_INDEX;
517 switch ( field ) {
518 case UCAL_ERA:
519 index = kIPI_ERA;
520 break;
521 case UCAL_YEAR:
522 index = kIPI_YEAR;
523 break;
524 case UCAL_MONTH:
525 index = kIPI_MONTH;
526 break;
527 case UCAL_DATE:
528 case UCAL_DAY_OF_WEEK:
529 //case UCAL_DAY_OF_MONTH:
530 index = kIPI_DATE;
531 break;
532 case UCAL_AM_PM:
533 index = kIPI_AM_PM;
534 break;
535 case UCAL_HOUR:
536 case UCAL_HOUR_OF_DAY:
537 index = kIPI_HOUR;
538 break;
539 case UCAL_MINUTE:
540 index = kIPI_MINUTE;
541 break;
542 default:
543 status = U_ILLEGAL_ARGUMENT_ERROR;
544 }
545 return index;
546 }
547
548
549
550 void
551 DateIntervalInfo::deleteHash(Hashtable* hTable)
552 {
553 if ( hTable == NULL ) {
554 return;
555 }
556 int32_t pos = -1;
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;
562 delete[] value;
563 }
564 delete fIntervalPatterns;
565 }
566
567
568 U_CDECL_BEGIN
569
570 /**
571 * set hash table value comparator
572 *
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
576 */
577 static UBool U_CALLCONV hashTableValueComparator(UHashTok val1, UHashTok val2);
578
579 U_CDECL_END
580
581 UBool
582 U_CALLCONV hashTableValueComparator(UHashTok val1, UHashTok val2) {
583 const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
584 const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
585 UBool ret = TRUE;
586 int8_t i;
587 for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX && ret == TRUE; ++i ) {
588 ret = (pattern1[i] == pattern2[i]);
589 }
590 return ret;
591 }
592
593
594
595 Hashtable*
596 DateIntervalInfo::initHash(UErrorCode& status) {
597 if ( U_FAILURE(status) ) {
598 return NULL;
599 }
600 Hashtable* hTable;
601 if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
602 status = U_MEMORY_ALLOCATION_ERROR;
603 return NULL;
604 }
605 hTable->setValueCompartor(hashTableValueComparator);
606 return hTable;
607 }
608
609
610 void
611 DateIntervalInfo::copyHash(const Hashtable* source,
612 Hashtable* target,
613 UErrorCode& status) {
614 if ( U_FAILURE(status) ) {
615 return;
616 }
617 int32_t pos = -1;
618 const UHashElement* element = NULL;
619 if ( source ) {
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];
626 int8_t i;
627 for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
628 copy[i] = value[i];
629 }
630 target->put(UnicodeString(*key), copy, status);
631 if ( U_FAILURE(status) ) {
632 return;
633 }
634 }
635 }
636 }
637
638
639 U_NAMESPACE_END
640
641 #endif