]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/dtitvinf.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / dtitvinf.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
46f4442e 3/*******************************************************************************
2ca993e8 4* Copyright (C) 2008-2016, International Business Machines Corporation and
46f4442e
A
5* others. All Rights Reserved.
6*******************************************************************************
7*
2ca993e8 8* File DTITVINF.CPP
46f4442e
A
9*
10*******************************************************************************
11*/
12
13#include "unicode/dtitvinf.h"
14
15
16#if !UCONFIG_NO_FORMATTING
17
729e4ab9 18//TODO: define it in compiler time
46f4442e
A
19//#define DTITVINF_DEBUG 1
20
21
2ca993e8 22#ifdef DTITVINF_DEBUG
46f4442e
A
23#include <iostream>
24#endif
25
2ca993e8 26#include "cmemory.h"
46f4442e
A
27#include "cstring.h"
28#include "unicode/msgfmt.h"
729e4ab9
A
29#include "unicode/uloc.h"
30#include "unicode/ures.h"
46f4442e 31#include "dtitv_impl.h"
2ca993e8 32#include "charstr.h"
46f4442e
A
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
2ca993e8 44#ifdef DTITVINF_DEBUG
46f4442e
A
45#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
46#endif
47
48UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
49
50static const char gCalendarTag[]="calendar";
51static const char gGregorianTag[]="gregorian";
52static const char gIntervalDateTimePatternTag[]="intervalFormats";
53static const char gFallbackPatternTag[]="fallback";
54
55// {0}
56static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
57// {1}
58static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
59
60// default fall-back
61static 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
2ca993e8 63DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
46f4442e
A
64: fFallbackIntervalPattern(gDefaultFallbackPattern),
65 fFirstDateInPtnIsLaterDate(false),
66 fIntervalPatterns(NULL)
67{
68 fIntervalPatterns = initHash(status);
69}
70
71
72
73DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
74: fFallbackIntervalPattern(gDefaultFallbackPattern),
75 fFirstDateInPtnIsLaterDate(false),
76 fIntervalPatterns(NULL)
77{
78 initializeData(locale, status);
79}
80
81
82
83void
84DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
85 UCalendarDateFields lrgDiffCalUnit,
86 const UnicodeString& intervalPattern,
87 UErrorCode& status) {
2ca993e8 88
46f4442e
A
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
101void
102DateIntervalInfo::setFallbackIntervalPattern(
103 const UnicodeString& fallbackPattern,
104 UErrorCode& status) {
105 if ( U_FAILURE(status) ) {
106 return;
107 }
2ca993e8
A
108 int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
109 UPRV_LENGTHOF(gFirstPattern), 0);
110 int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
111 UPRV_LENGTHOF(gSecondPattern), 0);
46f4442e
A
112 if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
113 status = U_ILLEGAL_ARGUMENT_ERROR;
114 return;
115 }
2ca993e8 116 if ( firstPatternIndex > secondPatternIndex ) {
46f4442e
A
117 fFirstDateInPtnIsLaterDate = true;
118 }
119 fFallbackIntervalPattern = fallbackPattern;
120}
121
122
123
124DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
125: UObject(dtitvinf),
126 fIntervalPatterns(NULL)
127{
128 *this = dtitvinf;
129}
2ca993e8 130
46f4442e
A
131
132
133DateIntervalInfo&
134DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
135 if ( this == &dtitvinf ) {
136 return *this;
137 }
2ca993e8 138
46f4442e
A
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;
2ca993e8 145 }
46f4442e
A
146
147 fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
148 fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
149 return *this;
150}
151
152
153DateIntervalInfo*
154DateIntervalInfo::clone() const {
155 return new DateIntervalInfo(*this);
156}
157
158
159DateIntervalInfo::~DateIntervalInfo() {
160 deleteHash(fIntervalPatterns);
161 fIntervalPatterns = NULL;
162}
163
164
165UBool
166DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
2ca993e8 167 UBool equal = (
46f4442e
A
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
179UnicodeString&
180DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
181 UCalendarDateFields field,
182 UnicodeString& result,
183 UErrorCode& status) const {
184 if ( U_FAILURE(status) ) {
185 return result;
186 }
2ca993e8 187
46f4442e
A
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
203UBool
204DateIntervalInfo::getDefaultOrder() const {
205 return fFirstDateInPtnIsLaterDate;
206}
207
208
209UnicodeString&
210DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
211 result = fFallbackIntervalPattern;
212 return result;
213}
214
729e4ab9 215#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
46f4442e 216
2ca993e8
A
217
218static const int32_t PATH_PREFIX_LENGTH = 17;
219static 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};
221static const int32_t PATH_SUFFIX_LENGTH = 16;
222static 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.
2ca993e8 227 */
f3c0d7a5 228struct DateIntervalInfo::DateIntervalSink : public ResourceSink {
2ca993e8 229
f3c0d7a5
A
230 // Output data
231 DateIntervalInfo &dateIntervalInfo;
2ca993e8 232
f3c0d7a5
A
233 // Next calendar type
234 UnicodeString nextCalendarType;
2ca993e8
A
235
236 DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
f3c0d7a5 237 : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { }
2ca993e8
A
238 virtual ~DateIntervalSink();
239
f3c0d7a5
A
240 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) {
241 if (U_FAILURE(errorCode)) { return; }
2ca993e8 242
f3c0d7a5
A
243 // Iterate over all the calendar entries and only pick the 'intervalFormats' table.
244 ResourceTable dateIntervalData = value.getTable(errorCode);
2ca993e8 245 if (U_FAILURE(errorCode)) { return; }
f3c0d7a5
A
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; }
2ca993e8 256
f3c0d7a5
A
257 nextCalendarType.remove();
258 getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
2ca993e8 259
f3c0d7a5
A
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 }
2ca993e8
A
278 }
279 }
280
f3c0d7a5
A
281 /**
282 * Processes the patterns for a skeleton table
283 */
284 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 }
2ca993e8 303 }
2ca993e8
A
304 }
305
306 /**
307 * Extracts the calendar type from the path.
308 */
309 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
f3c0d7a5
A
321 /**
322 * Validates and processes the pattern letter
323 */
324 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
1546d4af
A
329 if (c0 == 'G') {
330 return UCAL_ERA;
331 } else if (c0 == 'y') {
f3c0d7a5
A
332 return UCAL_YEAR;
333 } else if (c0 == 'M') {
334 return UCAL_MONTH;
335 } else if (c0 == 'd') {
336 return UCAL_DATE;
337 } else if (c0 == 'a') {
338 return UCAL_AM_PM;
339 } else if (c0 == 'h' || c0 == 'H') {
340 return UCAL_HOUR;
341 } else if (c0 == 'm') {
342 return UCAL_MINUTE;
343 }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
344 }
345 return UCAL_FIELD_COUNT;
346 }
347
348 /**
349 * Stores the interval pattern for the current skeleton in the internal data structure
350 * if it's not present.
351 */
352 void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit,
353 const ResourceValue &value, UErrorCode &errorCode) {
354 // Check if the pattern has already been stored on the data structure
355 IntervalPatternIndex index =
356 dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
357 if (U_FAILURE(errorCode)) { return; }
358
359 UnicodeString skeleton(currentSkeleton, -1, US_INV);
360 UnicodeString* patternsOfOneSkeleton =
361 (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton));
362
363 if (patternsOfOneSkeleton == NULL || patternsOfOneSkeleton[index].isEmpty()) {
364 UnicodeString pattern = value.getUnicodeString(errorCode);
365 dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
366 pattern, errorCode);
367 }
368 }
369
2ca993e8
A
370 const UnicodeString &getNextCalendarType() {
371 return nextCalendarType;
372 }
373
374 void resetNextCalendarType() {
375 nextCalendarType.setToBogus();
376 }
2ca993e8
A
377};
378
379// Virtual destructors must be defined out of line.
f3c0d7a5 380DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
2ca993e8
A
381
382
383
384void
385DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
46f4442e 386{
2ca993e8
A
387 fIntervalPatterns = initHash(status);
388 if (U_FAILURE(status)) {
46f4442e 389 return;
2ca993e8
A
390 }
391 const char *locName = locale.getName();
392
393 // Get the correct calendar type
394 const char * calendarTypeToUse = gGregorianTag; // initial default
395 char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
396 char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
397 // obtain a locale that always has the calendar key value that should be used
398 (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL,
729e4ab9 399 "calendar", "calendar", locName, NULL, FALSE, &status);
2ca993e8
A
400 localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
401 // now get the calendar key value from that locale
402 int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType,
403 ULOC_KEYWORDS_CAPACITY, &status);
404 if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
405 calendarTypeToUse = calendarType;
406 }
407 status = U_ZERO_ERROR;
408
409 // Instantiate the resource bundles
410 UResourceBundle *rb, *calBundle;
411 rb = ures_open(NULL, locName, &status);
412 if (U_FAILURE(status)) {
413 return;
4388f060 414 }
2ca993e8 415 calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, NULL, &status);
46f4442e 416
2ca993e8
A
417
418 if (U_SUCCESS(status)) {
419 UResourceBundle *calTypeBundle, *itvDtPtnResource;
420
421 // Get the fallback pattern
46f4442e
A
422 const UChar* resStr;
423 int32_t resStrLen = 0;
2ca993e8
A
424 calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &status);
425 itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
426 gIntervalDateTimePatternTag, NULL, &status);
427 resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag,
428 &resStrLen, &status);
46f4442e
A
429 if ( U_SUCCESS(status) ) {
430 UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
431 setFallbackIntervalPattern(pattern, status);
432 }
2ca993e8
A
433 ures_close(itvDtPtnResource);
434 ures_close(calTypeBundle);
46f4442e 435
4388f060 436
2ca993e8
A
437 // Instantiate the sink
438 DateIntervalSink sink(*this, calendarTypeToUse);
439 const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
440
441 // Already loaded calendar types
442 Hashtable loadedCalendarTypes(FALSE, status);
4388f060 443
2ca993e8
A
444 if (U_SUCCESS(status)) {
445 while (!calendarTypeToUseUString.isBogus()) {
446 // Set an error when a loop is detected
447 if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
448 status = U_INVALID_FORMAT_ERROR;
46f4442e
A
449 break;
450 }
4388f060 451
2ca993e8
A
452 // Register the calendar type to avoid loops
453 loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
454 if (U_FAILURE(status)) { break; }
455
456 // Get the calendar string
457 CharString calTypeBuffer;
458 calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
459 if (U_FAILURE(status)) { break; }
460 const char *calType = calTypeBuffer.data();
461
462 // Reset the next calendar type to load.
463 sink.resetNextCalendarType();
464
465 // Get all resources for this calendar type
f3c0d7a5 466 ures_getAllItemsWithFallback(calBundle, calType, sink, status);
46f4442e 467 }
46f4442e
A
468 }
469 }
4388f060 470
2ca993e8
A
471 // Close the opened resource bundles
472 ures_close(calBundle);
4388f060 473 ures_close(rb);
46f4442e
A
474}
475
46f4442e
A
476void
477DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
478 UCalendarDateFields lrgDiffCalUnit,
479 const UnicodeString& intervalPattern,
480 UErrorCode& status) {
481 IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
482 if ( U_FAILURE(status) ) {
483 return;
484 }
485 UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
486 UBool emptyHash = false;
487 if ( patternsOfOneSkeleton == NULL ) {
488 patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
489 emptyHash = true;
490 }
2ca993e8 491
46f4442e
A
492 patternsOfOneSkeleton[index] = intervalPattern;
493 if ( emptyHash == TRUE ) {
494 fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
495 }
496}
497
498
499
2ca993e8
A
500void
501DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
46f4442e
A
502 int32_t* skeletonFieldWidth) {
503 const int8_t PATTERN_CHAR_BASE = 0x41;
504 int32_t i;
505 for ( i = 0; i < skeleton.length(); ++i ) {
506 // it is an ASCII char in skeleton
2ca993e8 507 int8_t ch = (int8_t)skeleton.charAt(i);
46f4442e
A
508 ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
509 }
510}
511
512
513
2ca993e8 514UBool
46f4442e
A
515DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
516 char patternLetter) {
517 if ( patternLetter == 'M' ) {
729e4ab9
A
518 if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
519 (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
46f4442e
A
520 return true;
521 }
522 }
523 return false;
524}
525
526
527
2ca993e8 528const UnicodeString*
46f4442e
A
529DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
530 int8_t& bestMatchDistanceInfo) const {
531#ifdef DTITVINF_DEBUG
532 char result[1000];
533 char result_1[1000];
534 char mesg[2000];
535 skeleton.extract(0, skeleton.length(), result, "UTF-8");
536 sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result);
537 PRINTMESG(mesg)
538#endif
539
540
541 int32_t inputSkeletonFieldWidth[] =
542 {
543 // A B C D E F G H I J K L M N O
544 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
545 // P Q R S T U V W X Y Z
546 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
547 // a b c d e f g h i j k l m n o
548 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
549 // p q r s t u v w x y z
550 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
551 };
552
553 int32_t skeletonFieldWidth[] =
554 {
555 // A B C D E F G H I J K L M N O
556 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
557 // P Q R S T U V W X Y Z
558 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
559 // a b c d e f g h i j k l m n o
560 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
561 // p q r s t u v w x y z
562 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
563 };
564
565 const int32_t DIFFERENT_FIELD = 0x1000;
566 const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
567 const int32_t BASE = 0x41;
568 const UChar CHAR_V = 0x0076;
569 const UChar CHAR_Z = 0x007A;
570
571 // hack for 'v' and 'z'.
572 // resource bundle only have time skeletons ending with 'v',
573 // but not for time skeletons ending with 'z'.
574 UBool replaceZWithV = false;
2ca993e8 575 const UnicodeString* inputSkeleton = &skeleton;
46f4442e
A
576 UnicodeString copySkeleton;
577 if ( skeleton.indexOf(CHAR_Z) != -1 ) {
46f4442e 578 copySkeleton = skeleton;
4388f060 579 copySkeleton.findAndReplace(UnicodeString(CHAR_Z), UnicodeString(CHAR_V));
46f4442e
A
580 inputSkeleton = &copySkeleton;
581 replaceZWithV = true;
582 }
583
584 parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
585 int32_t bestDistance = MAX_POSITIVE_INT;
586 const UnicodeString* bestSkeleton = NULL;
587
588 // 0 means exact the same skeletons;
589 // 1 means having the same field, but with different length,
590 // 2 means only z/v differs
591 // -1 means having different field.
592 bestMatchDistanceInfo = 0;
2ca993e8 593 int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
46f4442e 594
b331163b 595 int32_t pos = UHASH_FIRST;
46f4442e
A
596 const UHashElement* elem = NULL;
597 while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) {
598 const UHashTok keyTok = elem->key;
3d1f044b 599 UnicodeString* newSkeleton = (UnicodeString*)keyTok.pointer;
46f4442e
A
600#ifdef DTITVINF_DEBUG
601 skeleton->extract(0, skeleton->length(), result, "UTF-8");
602 sprintf(mesg, "available skeletons: skeleton: %s; \n", result);
603 PRINTMESG(mesg)
604#endif
605
606 // clear skeleton field width
607 int8_t i;
608 for ( i = 0; i < fieldLength; ++i ) {
2ca993e8 609 skeletonFieldWidth[i] = 0;
46f4442e 610 }
3d1f044b 611 parseSkeleton(*newSkeleton, skeletonFieldWidth);
46f4442e
A
612 // calculate distance
613 int32_t distance = 0;
614 int8_t fieldDifference = 1;
615 for ( i = 0; i < fieldLength; ++i ) {
616 int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
617 int32_t fieldWidth = skeletonFieldWidth[i];
618 if ( inputFieldWidth == fieldWidth ) {
619 continue;
620 }
621 if ( inputFieldWidth == 0 ) {
622 fieldDifference = -1;
623 distance += DIFFERENT_FIELD;
624 } else if ( fieldWidth == 0 ) {
625 fieldDifference = -1;
626 distance += DIFFERENT_FIELD;
2ca993e8 627 } else if (stringNumeric(inputFieldWidth, fieldWidth,
46f4442e
A
628 (char)(i+BASE) ) ) {
629 distance += STRING_NUMERIC_DIFFERENCE;
630 } else {
2ca993e8
A
631 distance += (inputFieldWidth > fieldWidth) ?
632 (inputFieldWidth - fieldWidth) :
46f4442e
A
633 (fieldWidth - inputFieldWidth);
634 }
635 }
636 if ( distance < bestDistance ) {
3d1f044b 637 bestSkeleton = newSkeleton;
46f4442e
A
638 bestDistance = distance;
639 bestMatchDistanceInfo = fieldDifference;
640 }
641 if ( distance == 0 ) {
642 bestMatchDistanceInfo = 0;
643 break;
644 }
645 }
646 if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
647 bestMatchDistanceInfo = 2;
648 }
649 return bestSkeleton;
650}
651
652
653
654DateIntervalInfo::IntervalPatternIndex
2ca993e8 655DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
46f4442e
A
656 UErrorCode& status) {
657 if ( U_FAILURE(status) ) {
658 return kIPI_MAX_INDEX;
659 }
660 IntervalPatternIndex index = kIPI_MAX_INDEX;
661 switch ( field ) {
662 case UCAL_ERA:
663 index = kIPI_ERA;
664 break;
665 case UCAL_YEAR:
666 index = kIPI_YEAR;
667 break;
668 case UCAL_MONTH:
669 index = kIPI_MONTH;
670 break;
671 case UCAL_DATE:
672 case UCAL_DAY_OF_WEEK:
673 //case UCAL_DAY_OF_MONTH:
674 index = kIPI_DATE;
675 break;
676 case UCAL_AM_PM:
677 index = kIPI_AM_PM;
678 break;
679 case UCAL_HOUR:
680 case UCAL_HOUR_OF_DAY:
681 index = kIPI_HOUR;
682 break;
683 case UCAL_MINUTE:
684 index = kIPI_MINUTE;
685 break;
b331163b
A
686 case UCAL_SECOND:
687 index = kIPI_SECOND;
688 break;
46f4442e
A
689 default:
690 status = U_ILLEGAL_ARGUMENT_ERROR;
691 }
692 return index;
693}
694
695
696
697void
2ca993e8 698DateIntervalInfo::deleteHash(Hashtable* hTable)
46f4442e
A
699{
700 if ( hTable == NULL ) {
701 return;
702 }
b331163b 703 int32_t pos = UHASH_FIRST;
46f4442e
A
704 const UHashElement* element = NULL;
705 while ( (element = hTable->nextElement(pos)) != NULL ) {
46f4442e
A
706 const UHashTok valueTok = element->value;
707 const UnicodeString* value = (UnicodeString*)valueTok.pointer;
708 delete[] value;
709 }
710 delete fIntervalPatterns;
711}
712
713
2ca993e8 714U_CDECL_BEGIN
46f4442e
A
715
716/**
717 * set hash table value comparator
718 *
719 * @param val1 one value in comparison
720 * @param val2 the other value in comparison
721 * @return TRUE if 2 values are the same, FALSE otherwise
722 */
729e4ab9 723static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
46f4442e 724
2ca993e8 725static UBool
729e4ab9 726U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
46f4442e
A
727 const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
728 const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
729 UBool ret = TRUE;
730 int8_t i;
729e4ab9 731 for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) {
46f4442e
A
732 ret = (pattern1[i] == pattern2[i]);
733 }
734 return ret;
735}
736
729e4ab9 737U_CDECL_END
46f4442e
A
738
739
740Hashtable*
741DateIntervalInfo::initHash(UErrorCode& status) {
742 if ( U_FAILURE(status) ) {
743 return NULL;
744 }
745 Hashtable* hTable;
729e4ab9 746 if ( (hTable = new Hashtable(FALSE, status)) == NULL ) {
46f4442e
A
747 status = U_MEMORY_ALLOCATION_ERROR;
748 return NULL;
749 }
729e4ab9 750 if ( U_FAILURE(status) ) {
2ca993e8 751 delete hTable;
729e4ab9
A
752 return NULL;
753 }
754 hTable->setValueComparator(dtitvinfHashTableValueComparator);
46f4442e
A
755 return hTable;
756}
757
758
759void
760DateIntervalInfo::copyHash(const Hashtable* source,
761 Hashtable* target,
762 UErrorCode& status) {
763 if ( U_FAILURE(status) ) {
764 return;
765 }
b331163b 766 int32_t pos = UHASH_FIRST;
46f4442e
A
767 const UHashElement* element = NULL;
768 if ( source ) {
769 while ( (element = source->nextElement(pos)) != NULL ) {
770 const UHashTok keyTok = element->key;
771 const UnicodeString* key = (UnicodeString*)keyTok.pointer;
772 const UHashTok valueTok = element->value;
773 const UnicodeString* value = (UnicodeString*)valueTok.pointer;
774 UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
775 int8_t i;
776 for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
777 copy[i] = value[i];
778 }
779 target->put(UnicodeString(*key), copy, status);
780 if ( U_FAILURE(status) ) {
781 return;
782 }
783 }
784 }
785}
786
787
788U_NAMESPACE_END
789
790#endif