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