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