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
41U_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
50UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
51
52static const char gCalendarTag[]="calendar";
53static const char gGenericTag[]="generic";
54static const char gGregorianTag[]="gregorian";
55static const char gIntervalDateTimePatternTag[]="intervalFormats";
56static const char gFallbackPatternTag[]="fallback";
57
58// {0}
59static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
60// {1}
61static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
62
63// default fall-back
64static const UChar gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0};
65
66DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
67: fFallbackIntervalPattern(gDefaultFallbackPattern),
68 fFirstDateInPtnIsLaterDate(false),
69 fIntervalPatterns(NULL)
70{
71 fIntervalPatterns = initHash(status);
72}
73
74
75
76DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
77: fFallbackIntervalPattern(gDefaultFallbackPattern),
78 fFirstDateInPtnIsLaterDate(false),
79 fIntervalPatterns(NULL)
80{
81 initializeData(locale, status);
82}
83
84
85
86void
87DateIntervalInfo::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
104void
105DateIntervalInfo::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
127DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
128: UObject(dtitvinf),
129 fIntervalPatterns(NULL)
130{
131 *this = dtitvinf;
132}
133
134
135
136DateIntervalInfo&
137DateIntervalInfo::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
156DateIntervalInfo*
157DateIntervalInfo::clone() const {
158 return new DateIntervalInfo(*this);
159}
160
161
162DateIntervalInfo::~DateIntervalInfo() {
163 deleteHash(fIntervalPatterns);
164 fIntervalPatterns = NULL;
165}
166
167
168UBool
169DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
170 UBool equal = (
171 fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
172 fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
173
174 if ( equal == TRUE ) {
175 equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
176 }
177
178 return equal;
179}
180
181
182UnicodeString&
183DateIntervalInfo::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 != NULL ) {
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
206UBool
207DateIntervalInfo::getDefaultOrder() const {
208 return fFirstDateInPtnIsLaterDate;
209}
210
211
212UnicodeString&
213DateIntervalInfo::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
221static const int32_t PATH_PREFIX_LENGTH = 17;
222static const UChar 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};
224static const int32_t PATH_SUFFIX_LENGTH = 16;
225static const UChar 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 */
231struct DateIntervalInfo::DateIntervalSink : public ResourceSink {
232
233 // Output data
234 DateIntervalInfo &dateIntervalInfo;
235
236 // Next calendar type
237 UnicodeString nextCalendarType;
238
239 DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
240 : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { }
241 virtual ~DateIntervalSink();
242
243 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) {
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 */
287 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 */
312 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 */
327 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 == 'h' || c0 == 'H') {
343 return UCAL_HOUR;
344 } else if (c0 == 'm') {
345 return UCAL_MINUTE;
346 }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
347 }
348 return UCAL_FIELD_COUNT;
349 }
350
351 /**
352 * Stores the interval pattern for the current skeleton in the internal data structure
353 * if it's not present.
354 */
355 void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit,
356 const ResourceValue &value, UErrorCode &errorCode) {
357 // Check if the pattern has already been stored on the data structure
358 IntervalPatternIndex index =
359 dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
360 if (U_FAILURE(errorCode)) { return; }
361
362 UnicodeString skeleton(currentSkeleton, -1, US_INV);
363 UnicodeString* patternsOfOneSkeleton =
364 (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton));
365
366 if (patternsOfOneSkeleton == NULL || patternsOfOneSkeleton[index].isEmpty()) {
367 UnicodeString pattern = value.getUnicodeString(errorCode);
368 dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
369 pattern, errorCode);
370 }
371 }
372
373 const UnicodeString &getNextCalendarType() {
374 return nextCalendarType;
375 }
376
377 void resetNextCalendarType() {
378 nextCalendarType.setToBogus();
379 }
380};
381
382// Virtual destructors must be defined out of line.
383DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
384
385
386
387void
388DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
389{
390 fIntervalPatterns = initHash(status);
391 if (U_FAILURE(status)) {
392 return;
393 }
394 const char *locName = locale.getName();
395
396 // Get the correct calendar type
397 const char * calendarTypeToUse = gGregorianTag; // initial default
398 char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
399 char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
400 // obtain a locale that always has the calendar key value that should be used
401 (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL,
402 "calendar", "calendar", locName, NULL, FALSE, &status);
403 localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
404 // now get the calendar key value from that locale
405 int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType,
406 ULOC_KEYWORDS_CAPACITY, &status);
407 if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
408 calendarTypeToUse = calendarType;
409 }
410 status = U_ZERO_ERROR;
411
412 // Instantiate the resource bundles
413 UResourceBundle *rb, *calBundle;
414 rb = ures_open(NULL, locName, &status);
415 if (U_FAILURE(status)) {
416 return;
417 }
418 calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, NULL, &status);
419
420
421 if (U_SUCCESS(status)) {
422 UResourceBundle *calTypeBundle, *itvDtPtnResource;
423
424 // Get the fallback pattern
425 const UChar* resStr;
426 int32_t resStrLen = 0;
427 calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &status);
428 itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
429 gIntervalDateTimePatternTag, NULL, &status);
430
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 if ( U_FAILURE(status) ) {
437 // Try to find "fallback" from "generic" to work around the bug in
438 // ures_getByKeyWithFallback
439 UErrorCode localStatus = U_ZERO_ERROR;
440 UResourceBundle *genericCalBundle =
441 ures_getByKeyWithFallback(calBundle, gGenericTag, NULL, &localStatus);
442 UResourceBundle *genericItvDtPtnResource =
443 ures_getByKeyWithFallback(
444 genericCalBundle, gIntervalDateTimePatternTag, NULL, &localStatus);
445 resStr = ures_getStringByKeyWithFallback(
446 genericItvDtPtnResource, gFallbackPatternTag, &resStrLen, &localStatus);
447 ures_close(genericItvDtPtnResource);
448 ures_close(genericCalBundle);
449 if ( U_SUCCESS(localStatus) ) {
450 status = U_USING_FALLBACK_WARNING;;
451 }
452 }
453 }
454
455 if ( U_SUCCESS(status) && (resStr != nullptr)) {
456 UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
457 setFallbackIntervalPattern(pattern, status);
458 }
459 ures_close(itvDtPtnResource);
460 ures_close(calTypeBundle);
461
462
463 // Instantiate the sink
464 DateIntervalSink sink(*this, calendarTypeToUse);
465 const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
466
467 // Already loaded calendar types
468 Hashtable loadedCalendarTypes(FALSE, status);
469
470 if (U_SUCCESS(status)) {
471 while (!calendarTypeToUseUString.isBogus()) {
472 // Set an error when a loop is detected
473 if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
474 status = U_INVALID_FORMAT_ERROR;
475 break;
476 }
477
478 // Register the calendar type to avoid loops
479 loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
480 if (U_FAILURE(status)) { break; }
481
482 // Get the calendar string
483 CharString calTypeBuffer;
484 calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
485 if (U_FAILURE(status)) { break; }
486 const char *calType = calTypeBuffer.data();
487
488 // Reset the next calendar type to load.
489 sink.resetNextCalendarType();
490
491 // Get all resources for this calendar type
492 ures_getAllItemsWithFallback(calBundle, calType, sink, status);
493 }
494 }
495 }
496
497 // Close the opened resource bundles
498 ures_close(calBundle);
499 ures_close(rb);
500}
501
502void
503DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
504 UCalendarDateFields lrgDiffCalUnit,
505 const UnicodeString& intervalPattern,
506 UErrorCode& status) {
507 IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
508 if ( U_FAILURE(status) ) {
509 return;
510 }
511 UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
512 UBool emptyHash = false;
513 if ( patternsOfOneSkeleton == NULL ) {
514 patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
515 emptyHash = true;
516 }
517
518 patternsOfOneSkeleton[index] = intervalPattern;
519 if ( emptyHash == TRUE ) {
520 fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
521 }
522}
523
524
525
526void
527DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
528 int32_t* skeletonFieldWidth) {
529 const int8_t PATTERN_CHAR_BASE = 0x41;
530 int32_t i;
531 for ( i = 0; i < skeleton.length(); ++i ) {
532 // it is an ASCII char in skeleton
533 int8_t ch = (int8_t)skeleton.charAt(i);
534 ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
535 }
536}
537
538
539
540UBool
541DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
542 char patternLetter) {
543 if ( patternLetter == 'M' ) {
544 if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
545 (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
546 return true;
547 }
548 }
549 return false;
550}
551
552
553
554const UnicodeString*
555DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
556 int8_t& bestMatchDistanceInfo) const {
557#ifdef DTITVINF_DEBUG
558 char result[1000];
559 char result_1[1000];
560 char mesg[2000];
561 skeleton.extract(0, skeleton.length(), result, "UTF-8");
562 sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result);
563 PRINTMESG(mesg)
564#endif
565
566
567 int32_t inputSkeletonFieldWidth[] =
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 int32_t skeletonFieldWidth[] =
580 {
581 // A B C D E F G H I J K L M N O
582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
583 // P Q R S T U V W X Y Z
584 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
585 // a b c d e f g h i j k l m n o
586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
587 // p q r s t u v w x y z
588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
589 };
590
591 const int32_t DIFFERENT_FIELD = 0x1000;
592 const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
593 const int32_t BASE = 0x41;
594 const UChar CHAR_V = 0x0076;
595 const UChar CHAR_Z = 0x007A;
596
597 // hack for 'v' and 'z'.
598 // resource bundle only have time skeletons ending with 'v',
599 // but not for time skeletons ending with 'z'.
600 UBool replaceZWithV = false;
601 const UnicodeString* inputSkeleton = &skeleton;
602 UnicodeString copySkeleton;
603 if ( skeleton.indexOf(CHAR_Z) != -1 ) {
604 copySkeleton = skeleton;
605 copySkeleton.findAndReplace(UnicodeString(CHAR_Z), UnicodeString(CHAR_V));
606 inputSkeleton = &copySkeleton;
607 replaceZWithV = true;
608 }
609
610 parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
611 int32_t bestDistance = MAX_POSITIVE_INT;
612 const UnicodeString* bestSkeleton = NULL;
613
614 // 0 means exact the same skeletons;
615 // 1 means having the same field, but with different length,
616 // 2 means only z/v differs
617 // -1 means having different field.
618 bestMatchDistanceInfo = 0;
619 int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
620
621 int32_t pos = UHASH_FIRST;
622 const UHashElement* elem = NULL;
623 while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) {
624 const UHashTok keyTok = elem->key;
625 UnicodeString* newSkeleton = (UnicodeString*)keyTok.pointer;
626#ifdef DTITVINF_DEBUG
627 skeleton->extract(0, skeleton->length(), result, "UTF-8");
628 sprintf(mesg, "available skeletons: skeleton: %s; \n", result);
629 PRINTMESG(mesg)
630#endif
631
632 // clear skeleton field width
633 int8_t i;
634 for ( i = 0; i < fieldLength; ++i ) {
635 skeletonFieldWidth[i] = 0;
636 }
637 parseSkeleton(*newSkeleton, skeletonFieldWidth);
638 // calculate distance
639 int32_t distance = 0;
640 int8_t fieldDifference = 1;
641 for ( i = 0; i < fieldLength; ++i ) {
642 int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
643 int32_t fieldWidth = skeletonFieldWidth[i];
644 if ( inputFieldWidth == fieldWidth ) {
645 continue;
646 }
647 if ( inputFieldWidth == 0 ) {
648 fieldDifference = -1;
649 distance += DIFFERENT_FIELD;
650 } else if ( fieldWidth == 0 ) {
651 fieldDifference = -1;
652 distance += DIFFERENT_FIELD;
653 } else if (stringNumeric(inputFieldWidth, fieldWidth,
654 (char)(i+BASE) ) ) {
655 distance += STRING_NUMERIC_DIFFERENCE;
656 } else {
657 distance += (inputFieldWidth > fieldWidth) ?
658 (inputFieldWidth - fieldWidth) :
659 (fieldWidth - inputFieldWidth);
660 }
661 }
662 if ( distance < bestDistance ) {
663 bestSkeleton = newSkeleton;
664 bestDistance = distance;
665 bestMatchDistanceInfo = fieldDifference;
666 }
667 if ( distance == 0 ) {
668 bestMatchDistanceInfo = 0;
669 break;
670 }
671 }
672 if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
673 bestMatchDistanceInfo = 2;
674 }
675 return bestSkeleton;
676}
677
678
679
680DateIntervalInfo::IntervalPatternIndex
681DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
682 UErrorCode& status) {
683 if ( U_FAILURE(status) ) {
684 return kIPI_MAX_INDEX;
685 }
686 IntervalPatternIndex index = kIPI_MAX_INDEX;
687 switch ( field ) {
688 case UCAL_ERA:
689 index = kIPI_ERA;
690 break;
691 case UCAL_YEAR:
692 index = kIPI_YEAR;
693 break;
694 case UCAL_MONTH:
695 index = kIPI_MONTH;
696 break;
697 case UCAL_DATE:
698 case UCAL_DAY_OF_WEEK:
699 //case UCAL_DAY_OF_MONTH:
700 index = kIPI_DATE;
701 break;
702 case UCAL_AM_PM:
703 index = kIPI_AM_PM;
704 break;
705 case UCAL_HOUR:
706 case UCAL_HOUR_OF_DAY:
707 index = kIPI_HOUR;
708 break;
709 case UCAL_MINUTE:
710 index = kIPI_MINUTE;
711 break;
712 case UCAL_SECOND:
713 index = kIPI_SECOND;
714 break;
715 default:
716 status = U_ILLEGAL_ARGUMENT_ERROR;
717 }
718 return index;
719}
720
721
722
723void
724DateIntervalInfo::deleteHash(Hashtable* hTable)
725{
726 if ( hTable == NULL ) {
727 return;
728 }
729 int32_t pos = UHASH_FIRST;
730 const UHashElement* element = NULL;
731 while ( (element = hTable->nextElement(pos)) != NULL ) {
732 const UHashTok valueTok = element->value;
733 const UnicodeString* value = (UnicodeString*)valueTok.pointer;
734 delete[] value;
735 }
736 delete fIntervalPatterns;
737}
738
739
740U_CDECL_BEGIN
741
742/**
743 * set hash table value comparator
744 *
745 * @param val1 one value in comparison
746 * @param val2 the other value in comparison
747 * @return TRUE if 2 values are the same, FALSE otherwise
748 */
749static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
750
751static UBool
752U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
753 const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
754 const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
755 UBool ret = TRUE;
756 int8_t i;
757 for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) {
758 ret = (pattern1[i] == pattern2[i]);
759 }
760 return ret;
761}
762
763U_CDECL_END
764
765
766Hashtable*
767DateIntervalInfo::initHash(UErrorCode& status) {
768 if ( U_FAILURE(status) ) {
769 return NULL;
770 }
771 Hashtable* hTable;
772 if ( (hTable = new Hashtable(FALSE, status)) == NULL ) {
773 status = U_MEMORY_ALLOCATION_ERROR;
774 return NULL;
775 }
776 if ( U_FAILURE(status) ) {
777 delete hTable;
778 return NULL;
779 }
780 hTable->setValueComparator(dtitvinfHashTableValueComparator);
781 return hTable;
782}
783
784
785void
786DateIntervalInfo::copyHash(const Hashtable* source,
787 Hashtable* target,
788 UErrorCode& status) {
789 if ( U_FAILURE(status) ) {
790 return;
791 }
792 int32_t pos = UHASH_FIRST;
793 const UHashElement* element = NULL;
794 if ( source ) {
795 while ( (element = source->nextElement(pos)) != NULL ) {
796 const UHashTok keyTok = element->key;
797 const UnicodeString* key = (UnicodeString*)keyTok.pointer;
798 const UHashTok valueTok = element->value;
799 const UnicodeString* value = (UnicodeString*)valueTok.pointer;
800 UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
801 int8_t i;
802 for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
803 copy[i] = value[i];
804 }
805 target->put(UnicodeString(*key), copy, status);
806 if ( U_FAILURE(status) ) {
807 return;
808 }
809 }
810 }
811}
812
813
814U_NAMESPACE_END
815
816#endif
817