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