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