1 /*******************************************************************************
2 * Copyright (C) 2008-2016, 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 //TODO: define it in compiler time
17 //#define DTITVINF_DEBUG 1
18
19
20 #ifdef DTITVINF_DEBUG
21 #include <iostream>
22 #endif
23
24 #include "cmemory.h"
25 #include "cstring.h"
26 #include "unicode/msgfmt.h"
27 #include "unicode/uloc.h"
28 #include "unicode/ures.h"
29 #include "dtitv_impl.h"
30 #include "charstr.h"
31 #include "hash.h"
32 #include "gregoimp.h"
33 #include "uresimp.h"
34 #include "hash.h"
35 #include "gregoimp.h"
36 #include "uresimp.h"
37
38
39 U_NAMESPACE_BEGIN
40
41
42 #ifdef DTITVINF_DEBUG
43 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
44 #endif
45
46 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
47
48 static const char gCalendarTag[]="calendar";
49 static const char gGregorianTag[]="gregorian";
50 static const char gIntervalDateTimePatternTag[]="intervalFormats";
51 static const char gFallbackPatternTag[]="fallback";
52
53 // {0}
54 static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
55 // {1}
56 static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
57
58 // default fall-back
59 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};
60
DateIntervalInfo(UErrorCode & status)61 DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
62 : fFallbackIntervalPattern(gDefaultFallbackPattern),
63 fFirstDateInPtnIsLaterDate(false),
64 fIntervalPatterns(NULL)
65 {
66 fIntervalPatterns = initHash(status);
67 }
68
69
70
DateIntervalInfo(const Locale & locale,UErrorCode & status)71 DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
72 : fFallbackIntervalPattern(gDefaultFallbackPattern),
73 fFirstDateInPtnIsLaterDate(false),
74 fIntervalPatterns(NULL)
75 {
76 initializeData(locale, status);
77 }
78
79
80
81 void
setIntervalPattern(const UnicodeString & skeleton,UCalendarDateFields lrgDiffCalUnit,const UnicodeString & intervalPattern,UErrorCode & status)82 DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
83 UCalendarDateFields lrgDiffCalUnit,
84 const UnicodeString& intervalPattern,
85 UErrorCode& status) {
86
87 if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) {
88 setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status);
89 setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status);
90 } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH ||
91 lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) {
92 setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status);
93 } else {
94 setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
95 }
96 }
97
98
99 void
setFallbackIntervalPattern(const UnicodeString & fallbackPattern,UErrorCode & status)100 DateIntervalInfo::setFallbackIntervalPattern(
101 const UnicodeString& fallbackPattern,
102 UErrorCode& status) {
103 if ( U_FAILURE(status) ) {
104 return;
105 }
106 int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
107 UPRV_LENGTHOF(gFirstPattern), 0);
108 int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
109 UPRV_LENGTHOF(gSecondPattern), 0);
110 if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
111 status = U_ILLEGAL_ARGUMENT_ERROR;
112 return;
113 }
114 if ( firstPatternIndex > secondPatternIndex ) {
115 fFirstDateInPtnIsLaterDate = true;
116 }
117 fFallbackIntervalPattern = fallbackPattern;
118 }
119
120
121
DateIntervalInfo(const DateIntervalInfo & dtitvinf)122 DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
123 : UObject(dtitvinf),
124 fIntervalPatterns(NULL)
125 {
126 *this = dtitvinf;
127 }
128
129
130
131 DateIntervalInfo&
operator =(const DateIntervalInfo & dtitvinf)132 DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
133 if ( this == &dtitvinf ) {
134 return *this;
135 }
136
137 UErrorCode status = U_ZERO_ERROR;
138 deleteHash(fIntervalPatterns);
139 fIntervalPatterns = initHash(status);
140 copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
141 if ( U_FAILURE(status) ) {
142 return *this;
143 }
144
145 fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
146 fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
147 return *this;
148 }
149
150
151 DateIntervalInfo*
clone() const152 DateIntervalInfo::clone() const {
153 return new DateIntervalInfo(*this);
154 }
155
156
~DateIntervalInfo()157 DateIntervalInfo::~DateIntervalInfo() {
158 deleteHash(fIntervalPatterns);
159 fIntervalPatterns = NULL;
160 }
161
162
163 UBool
operator ==(const DateIntervalInfo & other) const164 DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
165 UBool equal = (
166 fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
167 fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
168
169 if ( equal == TRUE ) {
170 equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
171 }
172
173 return equal;
174 }
175
176
177 UnicodeString&
getIntervalPattern(const UnicodeString & skeleton,UCalendarDateFields field,UnicodeString & result,UErrorCode & status) const178 DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
179 UCalendarDateFields field,
180 UnicodeString& result,
181 UErrorCode& status) const {
182 if ( U_FAILURE(status) ) {
183 return result;
184 }
185
186 const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
187 if ( patternsOfOneSkeleton != NULL ) {
188 IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
189 if ( U_FAILURE(status) ) {
190 return result;
191 }
192 const UnicodeString& intervalPattern = patternsOfOneSkeleton[index];
193 if ( !intervalPattern.isEmpty() ) {
194 result = intervalPattern;
195 }
196 }
197 return result;
198 }
199
200
201 UBool
getDefaultOrder() const202 DateIntervalInfo::getDefaultOrder() const {
203 return fFirstDateInPtnIsLaterDate;
204 }
205
206
207 UnicodeString&
getFallbackIntervalPattern(UnicodeString & result) const208 DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
209 result = fFallbackIntervalPattern;
210 return result;
211 }
212
213 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
214
215
216 static const int32_t PATH_PREFIX_LENGTH = 17;
217 static const UChar PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS,
218 LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS};
219 static const int32_t PATH_SUFFIX_LENGTH = 16;
220 static const UChar PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A,
221 LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S};
222
223 /**
224 * Sink for enumerating all of the date interval skeletons.
225 * Contains inner sink structs, each one corresponding to a type of resource table.
226 * The outer struct finds the dateInterval table or an alias.
227 */
228 struct DateIntervalSink : public ResourceTableSink {
229
230 /**
231 * Sink to handle each skeleton table.
232 */
233 struct SkeletonSink : public ResourceTableSink {
SkeletonSinkDateIntervalSink::SkeletonSink234 SkeletonSink(DateIntervalSink &sink) : outer(sink) {}
235 virtual ~SkeletonSink();
236
getOrCreateTableSinkDateIntervalSink::SkeletonSink237 virtual ResourceTableSink *getOrCreateTableSink(
238 const char *key, int32_t, UErrorCode &errorCode) {
239 if (U_SUCCESS(errorCode)) {
240 outer.currentSkeleton = key;
241 return &outer.patternSink;
242 }
243 return NULL;
244 }
245
246 DateIntervalSink &outer;
247 } skeletonSink;
248
249 /**
250 * Sink to store the date interval pattern for each skeleton pattern character.
251 */
252 struct PatternSink : public ResourceTableSink {
PatternSinkDateIntervalSink::PatternSink253 PatternSink(DateIntervalSink &sink) : outer(sink) {}
254 virtual ~PatternSink();
255
putDateIntervalSink::PatternSink256 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
257 if (U_FAILURE(errorCode)) { return; }
258
259 // Process the key
260 UCalendarDateFields calendarField = validateAndProcessPatternLetter(key);
261
262 // If the calendar field has a valid value
263 if (calendarField < UCAL_FIELD_COUNT) {
264 // Set the interval pattern
265 setIntervalPatternIfAbsent(calendarField, value, errorCode);
266 } else {
267 errorCode = U_INVALID_FORMAT_ERROR;
268 }
269 }
270
validateAndProcessPatternLetterDateIntervalSink::PatternSink271 UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) {
272 // Check that patternLetter is just one letter
273 char c0;
274 if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
275 // Check that the pattern letter is accepted
276 if (c0 == 'y') {
277 return UCAL_YEAR;
278 } else if (c0 == 'M') {
279 return UCAL_MONTH;
280 } else if (c0 == 'd') {
281 return UCAL_DATE;
282 } else if (c0 == 'a') {
283 return UCAL_AM_PM;
284 } else if (c0 == 'h' || c0 == 'H') {
285 return UCAL_HOUR;
286 } else if (c0 == 'm') {
287 return UCAL_MINUTE;
288 }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
289 }
290 return UCAL_FIELD_COUNT;
291 }
292
293 /**
294 * Stores the interval pattern for the current skeleton in the internal data structure
295 * if it's not present.
296 */
setIntervalPatternIfAbsentDateIntervalSink::PatternSink297 void setIntervalPatternIfAbsent(UCalendarDateFields lrgDiffCalUnit,
298 const ResourceValue &value, UErrorCode &errorCode) {
299 // Check if the pattern has already been stored on the data structure
300 DateIntervalInfo::IntervalPatternIndex index =
301 outer.dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
302 if (U_FAILURE(errorCode)) { return; }
303
304 UnicodeString skeleton(outer.currentSkeleton, -1, US_INV);
305 UnicodeString* patternsOfOneSkeleton =
306 (UnicodeString*)(outer.dateIntervalInfo.fIntervalPatterns->get(skeleton));
307
308 if (patternsOfOneSkeleton == NULL || patternsOfOneSkeleton[index].isEmpty()) {
309 UnicodeString pattern = value.getUnicodeString(errorCode);
310 outer.dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
311 pattern, errorCode);
312 }
313 }
314
315 DateIntervalSink &outer;
316 } patternSink;
317
318
DateIntervalSinkDateIntervalSink319 DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
320 : skeletonSink(*this), patternSink(*this), dateIntervalInfo(diInfo),
321 nextCalendarType(currentCalendarType, -1, US_INV), currentSkeleton(NULL) { }
322 virtual ~DateIntervalSink();
323
putDateIntervalSink324 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
325 // Check if it's an alias of intervalFormats
326 if (U_FAILURE(errorCode) || value.getType() != URES_ALIAS
327 || uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) {
328 return;
329 }
330
331 // Get the calendar type for the alias path.
332 const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode);
333 if (U_FAILURE(errorCode)) { return; }
334
335 nextCalendarType.remove();
336 getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
337
338 if (U_FAILURE(errorCode)) {
339 resetNextCalendarType();
340 }
341 }
342
getOrCreateTableSinkDateIntervalSink343 virtual ResourceTableSink *getOrCreateTableSink(
344 const char *key, int32_t, UErrorCode &errorCode) {
345 // Check if it's the intervalFormat table
346 if (U_SUCCESS(errorCode) && uprv_strcmp(key, gIntervalDateTimePatternTag) == 0) {
347 return &skeletonSink;
348 }
349 return NULL;
350 }
351
352 /**
353 * Extracts the calendar type from the path.
354 */
getCalendarTypeFromPathDateIntervalSink355 static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType,
356 UErrorCode &errorCode) {
357 if (U_FAILURE(errorCode)) { return; }
358
359 if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) {
360 errorCode = U_INVALID_FORMAT_ERROR;
361 return;
362 }
363
364 path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType);
365 }
366
getNextCalendarTypeDateIntervalSink367 const UnicodeString &getNextCalendarType() {
368 return nextCalendarType;
369 }
370
resetNextCalendarTypeDateIntervalSink371 void resetNextCalendarType() {
372 nextCalendarType.setToBogus();
373 }
374
375
376 // Output data
377 DateIntervalInfo &dateIntervalInfo;
378
379 // Next calendar type
380 UnicodeString nextCalendarType;
381
382 // Current skeleton table being enumerated
383 const char *currentSkeleton;
384 };
385
386 // Virtual destructors must be defined out of line.
~SkeletonSink()387 DateIntervalSink::SkeletonSink::~SkeletonSink() {}
~PatternSink()388 DateIntervalSink::PatternSink::~PatternSink() {}
~DateIntervalSink()389 DateIntervalSink::~DateIntervalSink() {}
390
391
392
393 void
initializeData(const Locale & locale,UErrorCode & status)394 DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
395 {
396 fIntervalPatterns = initHash(status);
397 if (U_FAILURE(status)) {
398 return;
399 }
400 const char *locName = locale.getName();
401
402 // Get the correct calendar type
403 const char * calendarTypeToUse = gGregorianTag; // initial default
404 char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
405 char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
406 // obtain a locale that always has the calendar key value that should be used
407 (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL,
408 "calendar", "calendar", locName, NULL, FALSE, &status);
409 localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
410 // now get the calendar key value from that locale
411 int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType,
412 ULOC_KEYWORDS_CAPACITY, &status);
413 if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
414 calendarTypeToUse = calendarType;
415 }
416 status = U_ZERO_ERROR;
417
418 // Instantiate the resource bundles
419 UResourceBundle *rb, *calBundle;
420 rb = ures_open(NULL, locName, &status);
421 if (U_FAILURE(status)) {
422 return;
423 }
424 calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, NULL, &status);
425
426
427 if (U_SUCCESS(status)) {
428 UResourceBundle *calTypeBundle, *itvDtPtnResource;
429
430 // Get the fallback pattern
431 const UChar* resStr;
432 int32_t resStrLen = 0;
433 calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &status);
434 itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
435 gIntervalDateTimePatternTag, NULL, &status);
436 resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag,
437 &resStrLen, &status);
438 if ( U_SUCCESS(status) ) {
439 UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
440 setFallbackIntervalPattern(pattern, status);
441 }
442 ures_close(itvDtPtnResource);
443 ures_close(calTypeBundle);
444
445
446 // Instantiate the sink
447 DateIntervalSink sink(*this, calendarTypeToUse);
448 const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
449
450 // Already loaded calendar types
451 Hashtable loadedCalendarTypes(FALSE, status);
452
453 if (U_SUCCESS(status)) {
454 while (!calendarTypeToUseUString.isBogus()) {
455 // Set an error when a loop is detected
456 if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
457 status = U_INVALID_FORMAT_ERROR;
458 break;
459 }
460
461 // Register the calendar type to avoid loops
462 loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
463 if (U_FAILURE(status)) { break; }
464
465 // Get the calendar string
466 CharString calTypeBuffer;
467 calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
468 if (U_FAILURE(status)) { break; }
469 const char *calType = calTypeBuffer.data();
470
471 // Reset the next calendar type to load.
472 sink.resetNextCalendarType();
473
474 // Get all resources for this calendar type
475 ures_getAllTableItemsWithFallback(calBundle, calType, sink, status);
476 }
477 }
478 }
479
480 // Close the opened resource bundles
481 ures_close(calBundle);
482 ures_close(rb);
483 }
484
485 void
setIntervalPatternInternally(const UnicodeString & skeleton,UCalendarDateFields lrgDiffCalUnit,const UnicodeString & intervalPattern,UErrorCode & status)486 DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
487 UCalendarDateFields lrgDiffCalUnit,
488 const UnicodeString& intervalPattern,
489 UErrorCode& status) {
490 IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
491 if ( U_FAILURE(status) ) {
492 return;
493 }
494 UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
495 UBool emptyHash = false;
496 if ( patternsOfOneSkeleton == NULL ) {
497 patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
498 emptyHash = true;
499 }
500
501 patternsOfOneSkeleton[index] = intervalPattern;
502 if ( emptyHash == TRUE ) {
503 fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
504 }
505 }
506
507
508
509 void
parseSkeleton(const UnicodeString & skeleton,int32_t * skeletonFieldWidth)510 DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
511 int32_t* skeletonFieldWidth) {
512 const int8_t PATTERN_CHAR_BASE = 0x41;
513 int32_t i;
514 for ( i = 0; i < skeleton.length(); ++i ) {
515 // it is an ASCII char in skeleton
516 int8_t ch = (int8_t)skeleton.charAt(i);
517 ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
518 }
519 }
520
521
522
523 UBool
stringNumeric(int32_t fieldWidth,int32_t anotherFieldWidth,char patternLetter)524 DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
525 char patternLetter) {
526 if ( patternLetter == 'M' ) {
527 if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
528 (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
529 return true;
530 }
531 }
532 return false;
533 }
534
535
536
537 const UnicodeString*
getBestSkeleton(const UnicodeString & skeleton,int8_t & bestMatchDistanceInfo) const538 DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
539 int8_t& bestMatchDistanceInfo) const {
540 #ifdef DTITVINF_DEBUG
541 char result[1000];
542 char result_1[1000];
543 char mesg[2000];
544 skeleton.extract(0, skeleton.length(), result, "UTF-8");
545 sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result);
546 PRINTMESG(mesg)
547 #endif
548
549
550 int32_t inputSkeletonFieldWidth[] =
551 {
552 // A B C D E F G H I J K L M N O
553 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
554 // P Q R S T U V W X Y Z
555 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
556 // a b c d e f g h i j k l m n o
557 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
558 // p q r s t u v w x y z
559 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
560 };
561
562 int32_t skeletonFieldWidth[] =
563 {
564 // A B C D E F G H I J K L M N O
565 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
566 // P Q R S T U V W X Y Z
567 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
568 // a b c d e f g h i j k l m n o
569 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
570 // p q r s t u v w x y z
571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
572 };
573
574 const int32_t DIFFERENT_FIELD = 0x1000;
575 const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
576 const int32_t BASE = 0x41;
577 const UChar CHAR_V = 0x0076;
578 const UChar CHAR_Z = 0x007A;
579
580 // hack for 'v' and 'z'.
581 // resource bundle only have time skeletons ending with 'v',
582 // but not for time skeletons ending with 'z'.
583 UBool replaceZWithV = false;
584 const UnicodeString* inputSkeleton = &skeleton;
585 UnicodeString copySkeleton;
586 if ( skeleton.indexOf(CHAR_Z) != -1 ) {
587 copySkeleton = skeleton;
588 copySkeleton.findAndReplace(UnicodeString(CHAR_Z), UnicodeString(CHAR_V));
589 inputSkeleton = ©Skeleton;
590 replaceZWithV = true;
591 }
592
593 parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
594 int32_t bestDistance = MAX_POSITIVE_INT;
595 const UnicodeString* bestSkeleton = NULL;
596
597 // 0 means exact the same skeletons;
598 // 1 means having the same field, but with different length,
599 // 2 means only z/v differs
600 // -1 means having different field.
601 bestMatchDistanceInfo = 0;
602 int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
603
604 int32_t pos = UHASH_FIRST;
605 const UHashElement* elem = NULL;
606 while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) {
607 const UHashTok keyTok = elem->key;
608 UnicodeString* skeleton = (UnicodeString*)keyTok.pointer;
609 #ifdef DTITVINF_DEBUG
610 skeleton->extract(0, skeleton->length(), result, "UTF-8");
611 sprintf(mesg, "available skeletons: skeleton: %s; \n", result);
612 PRINTMESG(mesg)
613 #endif
614
615 // clear skeleton field width
616 int8_t i;
617 for ( i = 0; i < fieldLength; ++i ) {
618 skeletonFieldWidth[i] = 0;
619 }
620 parseSkeleton(*skeleton, skeletonFieldWidth);
621 // calculate distance
622 int32_t distance = 0;
623 int8_t fieldDifference = 1;
624 for ( i = 0; i < fieldLength; ++i ) {
625 int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
626 int32_t fieldWidth = skeletonFieldWidth[i];
627 if ( inputFieldWidth == fieldWidth ) {
628 continue;
629 }
630 if ( inputFieldWidth == 0 ) {
631 fieldDifference = -1;
632 distance += DIFFERENT_FIELD;
633 } else if ( fieldWidth == 0 ) {
634 fieldDifference = -1;
635 distance += DIFFERENT_FIELD;
636 } else if (stringNumeric(inputFieldWidth, fieldWidth,
637 (char)(i+BASE) ) ) {
638 distance += STRING_NUMERIC_DIFFERENCE;
639 } else {
640 distance += (inputFieldWidth > fieldWidth) ?
641 (inputFieldWidth - fieldWidth) :
642 (fieldWidth - inputFieldWidth);
643 }
644 }
645 if ( distance < bestDistance ) {
646 bestSkeleton = skeleton;
647 bestDistance = distance;
648 bestMatchDistanceInfo = fieldDifference;
649 }
650 if ( distance == 0 ) {
651 bestMatchDistanceInfo = 0;
652 break;
653 }
654 }
655 if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
656 bestMatchDistanceInfo = 2;
657 }
658 return bestSkeleton;
659 }
660
661
662
663 DateIntervalInfo::IntervalPatternIndex
calendarFieldToIntervalIndex(UCalendarDateFields field,UErrorCode & status)664 DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
665 UErrorCode& status) {
666 if ( U_FAILURE(status) ) {
667 return kIPI_MAX_INDEX;
668 }
669 IntervalPatternIndex index = kIPI_MAX_INDEX;
670 switch ( field ) {
671 case UCAL_ERA:
672 index = kIPI_ERA;
673 break;
674 case UCAL_YEAR:
675 index = kIPI_YEAR;
676 break;
677 case UCAL_MONTH:
678 index = kIPI_MONTH;
679 break;
680 case UCAL_DATE:
681 case UCAL_DAY_OF_WEEK:
682 //case UCAL_DAY_OF_MONTH:
683 index = kIPI_DATE;
684 break;
685 case UCAL_AM_PM:
686 index = kIPI_AM_PM;
687 break;
688 case UCAL_HOUR:
689 case UCAL_HOUR_OF_DAY:
690 index = kIPI_HOUR;
691 break;
692 case UCAL_MINUTE:
693 index = kIPI_MINUTE;
694 break;
695 case UCAL_SECOND:
696 index = kIPI_SECOND;
697 break;
698 default:
699 status = U_ILLEGAL_ARGUMENT_ERROR;
700 }
701 return index;
702 }
703
704
705
706 void
deleteHash(Hashtable * hTable)707 DateIntervalInfo::deleteHash(Hashtable* hTable)
708 {
709 if ( hTable == NULL ) {
710 return;
711 }
712 int32_t pos = UHASH_FIRST;
713 const UHashElement* element = NULL;
714 while ( (element = hTable->nextElement(pos)) != NULL ) {
715 const UHashTok valueTok = element->value;
716 const UnicodeString* value = (UnicodeString*)valueTok.pointer;
717 delete[] value;
718 }
719 delete fIntervalPatterns;
720 }
721
722
723 U_CDECL_BEGIN
724
725 /**
726 * set hash table value comparator
727 *
728 * @param val1 one value in comparison
729 * @param val2 the other value in comparison
730 * @return TRUE if 2 values are the same, FALSE otherwise
731 */
732 static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
733
734 static UBool
dtitvinfHashTableValueComparator(UHashTok val1,UHashTok val2)735 U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
736 const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
737 const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
738 UBool ret = TRUE;
739 int8_t i;
740 for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) {
741 ret = (pattern1[i] == pattern2[i]);
742 }
743 return ret;
744 }
745
746 U_CDECL_END
747
748
749 Hashtable*
initHash(UErrorCode & status)750 DateIntervalInfo::initHash(UErrorCode& status) {
751 if ( U_FAILURE(status) ) {
752 return NULL;
753 }
754 Hashtable* hTable;
755 if ( (hTable = new Hashtable(FALSE, status)) == NULL ) {
756 status = U_MEMORY_ALLOCATION_ERROR;
757 return NULL;
758 }
759 if ( U_FAILURE(status) ) {
760 delete hTable;
761 return NULL;
762 }
763 hTable->setValueComparator(dtitvinfHashTableValueComparator);
764 return hTable;
765 }
766
767
768 void
copyHash(const Hashtable * source,Hashtable * target,UErrorCode & status)769 DateIntervalInfo::copyHash(const Hashtable* source,
770 Hashtable* target,
771 UErrorCode& status) {
772 if ( U_FAILURE(status) ) {
773 return;
774 }
775 int32_t pos = UHASH_FIRST;
776 const UHashElement* element = NULL;
777 if ( source ) {
778 while ( (element = source->nextElement(pos)) != NULL ) {
779 const UHashTok keyTok = element->key;
780 const UnicodeString* key = (UnicodeString*)keyTok.pointer;
781 const UHashTok valueTok = element->value;
782 const UnicodeString* value = (UnicodeString*)valueTok.pointer;
783 UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
784 int8_t i;
785 for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
786 copy[i] = value[i];
787 }
788 target->put(UnicodeString(*key), copy, status);
789 if ( U_FAILURE(status) ) {
790 return;
791 }
792 }
793 }
794 }
795
796
797 U_NAMESPACE_END
798
799 #endif
800