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