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