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 = ©Skeleton;
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