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 = &copySkeleton;
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