Line data Source code
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 0 : 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 :
63 0 : DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
64 : : fFallbackIntervalPattern(gDefaultFallbackPattern),
65 : fFirstDateInPtnIsLaterDate(false),
66 0 : fIntervalPatterns(NULL)
67 : {
68 0 : fIntervalPatterns = initHash(status);
69 0 : }
70 :
71 :
72 :
73 0 : DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
74 : : fFallbackIntervalPattern(gDefaultFallbackPattern),
75 : fFirstDateInPtnIsLaterDate(false),
76 0 : fIntervalPatterns(NULL)
77 : {
78 0 : initializeData(locale, status);
79 0 : }
80 :
81 :
82 :
83 : void
84 0 : DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
85 : UCalendarDateFields lrgDiffCalUnit,
86 : const UnicodeString& intervalPattern,
87 : UErrorCode& status) {
88 :
89 0 : if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) {
90 0 : setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status);
91 0 : setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status);
92 0 : } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH ||
93 : lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) {
94 0 : setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status);
95 : } else {
96 0 : setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
97 : }
98 0 : }
99 :
100 :
101 : void
102 0 : DateIntervalInfo::setFallbackIntervalPattern(
103 : const UnicodeString& fallbackPattern,
104 : UErrorCode& status) {
105 0 : if ( U_FAILURE(status) ) {
106 0 : return;
107 : }
108 : int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
109 0 : UPRV_LENGTHOF(gFirstPattern), 0);
110 : int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
111 0 : UPRV_LENGTHOF(gSecondPattern), 0);
112 0 : if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
113 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
114 0 : return;
115 : }
116 0 : if ( firstPatternIndex > secondPatternIndex ) {
117 0 : fFirstDateInPtnIsLaterDate = true;
118 : }
119 0 : fFallbackIntervalPattern = fallbackPattern;
120 : }
121 :
122 :
123 :
124 0 : DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
125 : : UObject(dtitvinf),
126 0 : fIntervalPatterns(NULL)
127 : {
128 0 : *this = dtitvinf;
129 0 : }
130 :
131 :
132 :
133 : DateIntervalInfo&
134 0 : DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
135 0 : if ( this == &dtitvinf ) {
136 0 : return *this;
137 : }
138 :
139 0 : UErrorCode status = U_ZERO_ERROR;
140 0 : deleteHash(fIntervalPatterns);
141 0 : fIntervalPatterns = initHash(status);
142 0 : copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
143 0 : if ( U_FAILURE(status) ) {
144 0 : return *this;
145 : }
146 :
147 0 : fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
148 0 : fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
149 0 : return *this;
150 : }
151 :
152 :
153 : DateIntervalInfo*
154 0 : DateIntervalInfo::clone() const {
155 0 : return new DateIntervalInfo(*this);
156 : }
157 :
158 :
159 0 : DateIntervalInfo::~DateIntervalInfo() {
160 0 : deleteHash(fIntervalPatterns);
161 0 : fIntervalPatterns = NULL;
162 0 : }
163 :
164 :
165 : UBool
166 0 : DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
167 : UBool equal = (
168 0 : fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
169 0 : fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
170 :
171 0 : if ( equal == TRUE ) {
172 0 : equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
173 : }
174 :
175 0 : return equal;
176 : }
177 :
178 :
179 : UnicodeString&
180 0 : DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
181 : UCalendarDateFields field,
182 : UnicodeString& result,
183 : UErrorCode& status) const {
184 0 : if ( U_FAILURE(status) ) {
185 0 : return result;
186 : }
187 :
188 0 : const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
189 0 : if ( patternsOfOneSkeleton != NULL ) {
190 0 : IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
191 0 : if ( U_FAILURE(status) ) {
192 0 : return result;
193 : }
194 0 : const UnicodeString& intervalPattern = patternsOfOneSkeleton[index];
195 0 : if ( !intervalPattern.isEmpty() ) {
196 0 : result = intervalPattern;
197 : }
198 : }
199 0 : return result;
200 : }
201 :
202 :
203 : UBool
204 0 : DateIntervalInfo::getDefaultOrder() const {
205 0 : return fFirstDateInPtnIsLaterDate;
206 : }
207 :
208 :
209 : UnicodeString&
210 0 : DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
211 0 : result = fFallbackIntervalPattern;
212 0 : 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 :
236 0 : DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
237 0 : : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { }
238 : virtual ~DateIntervalSink();
239 :
240 0 : virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) {
241 0 : if (U_FAILURE(errorCode)) { return; }
242 :
243 : // Iterate over all the calendar entries and only pick the 'intervalFormats' table.
244 0 : ResourceTable dateIntervalData = value.getTable(errorCode);
245 0 : if (U_FAILURE(errorCode)) { return; }
246 0 : for (int32_t i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) {
247 0 : if (uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) {
248 0 : continue;
249 : }
250 :
251 : // Handle aliases and tables. Ignore the rest.
252 0 : if (value.getType() == URES_ALIAS) {
253 : // Get the calendar type for the alias path.
254 0 : const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode);
255 0 : if (U_FAILURE(errorCode)) { return; }
256 :
257 0 : nextCalendarType.remove();
258 0 : getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
259 :
260 0 : if (U_FAILURE(errorCode)) {
261 0 : resetNextCalendarType();
262 : }
263 0 : break;
264 :
265 0 : } else if (value.getType() == URES_TABLE) {
266 : // Iterate over all the skeletons in the 'intervalFormat' table.
267 0 : ResourceTable skeletonData = value.getTable(errorCode);
268 0 : if (U_FAILURE(errorCode)) { return; }
269 0 : for (int32_t j = 0; skeletonData.getKeyAndValue(j, key, value); j++) {
270 0 : if (value.getType() == URES_TABLE) {
271 : // Process the skeleton
272 0 : processSkeletonTable(key, value, errorCode);
273 0 : if (U_FAILURE(errorCode)) { return; }
274 : }
275 : }
276 0 : break;
277 : }
278 : }
279 : }
280 :
281 : /**
282 : * Processes the patterns for a skeleton table
283 : */
284 0 : void processSkeletonTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
285 0 : if (U_FAILURE(errorCode)) { return; }
286 :
287 : // Iterate over all the patterns in the current skeleton table
288 0 : const char *currentSkeleton = key;
289 0 : ResourceTable patternData = value.getTable(errorCode);
290 0 : if (U_FAILURE(errorCode)) { return; }
291 0 : for (int32_t k = 0; patternData.getKeyAndValue(k, key, value); k++) {
292 0 : if (value.getType() == URES_STRING) {
293 : // Process the key
294 0 : UCalendarDateFields calendarField = validateAndProcessPatternLetter(key);
295 :
296 : // If the calendar field has a valid value
297 0 : if (calendarField < UCAL_FIELD_COUNT) {
298 : // Set the interval pattern
299 0 : setIntervalPatternIfAbsent(currentSkeleton, calendarField, value, errorCode);
300 0 : if (U_FAILURE(errorCode)) { return; }
301 : }
302 : }
303 : }
304 : }
305 :
306 : /**
307 : * Extracts the calendar type from the path.
308 : */
309 0 : static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType,
310 : UErrorCode &errorCode) {
311 0 : if (U_FAILURE(errorCode)) { return; }
312 :
313 0 : if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) {
314 0 : errorCode = U_INVALID_FORMAT_ERROR;
315 0 : return;
316 : }
317 :
318 0 : path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType);
319 : }
320 :
321 : /**
322 : * Validates and processes the pattern letter
323 : */
324 0 : UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) {
325 : // Check that patternLetter is just one letter
326 : char c0;
327 0 : if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
328 : // Check that the pattern letter is accepted
329 0 : if (c0 == 'y') {
330 0 : return UCAL_YEAR;
331 0 : } else if (c0 == 'M') {
332 0 : return UCAL_MONTH;
333 0 : } else if (c0 == 'd') {
334 0 : return UCAL_DATE;
335 0 : } else if (c0 == 'a') {
336 0 : return UCAL_AM_PM;
337 0 : } else if (c0 == 'h' || c0 == 'H') {
338 0 : return UCAL_HOUR;
339 0 : } else if (c0 == 'm') {
340 0 : return UCAL_MINUTE;
341 : }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
342 : }
343 0 : 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 : */
350 0 : 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 0 : dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
355 0 : if (U_FAILURE(errorCode)) { return; }
356 :
357 0 : UnicodeString skeleton(currentSkeleton, -1, US_INV);
358 : UnicodeString* patternsOfOneSkeleton =
359 0 : (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton));
360 :
361 0 : if (patternsOfOneSkeleton == NULL || patternsOfOneSkeleton[index].isEmpty()) {
362 0 : UnicodeString pattern = value.getUnicodeString(errorCode);
363 0 : dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
364 0 : pattern, errorCode);
365 : }
366 : }
367 :
368 0 : const UnicodeString &getNextCalendarType() {
369 0 : return nextCalendarType;
370 : }
371 :
372 0 : void resetNextCalendarType() {
373 0 : nextCalendarType.setToBogus();
374 0 : }
375 : };
376 :
377 : // Virtual destructors must be defined out of line.
378 0 : DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
379 :
380 :
381 :
382 : void
383 0 : DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
384 : {
385 0 : fIntervalPatterns = initHash(status);
386 0 : if (U_FAILURE(status)) {
387 0 : return;
388 : }
389 0 : const char *locName = locale.getName();
390 :
391 : // Get the correct calendar type
392 0 : 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 0 : "calendar", "calendar", locName, NULL, FALSE, &status);
398 0 : 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 0 : ULOC_KEYWORDS_CAPACITY, &status);
402 0 : if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
403 0 : calendarTypeToUse = calendarType;
404 : }
405 0 : status = U_ZERO_ERROR;
406 :
407 : // Instantiate the resource bundles
408 : UResourceBundle *rb, *calBundle;
409 0 : rb = ures_open(NULL, locName, &status);
410 0 : if (U_FAILURE(status)) {
411 0 : return;
412 : }
413 0 : calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, NULL, &status);
414 :
415 :
416 0 : if (U_SUCCESS(status)) {
417 : UResourceBundle *calTypeBundle, *itvDtPtnResource;
418 :
419 : // Get the fallback pattern
420 : const UChar* resStr;
421 0 : int32_t resStrLen = 0;
422 0 : calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &status);
423 : itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
424 0 : gIntervalDateTimePatternTag, NULL, &status);
425 : resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag,
426 0 : &resStrLen, &status);
427 0 : if ( U_SUCCESS(status) ) {
428 0 : UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
429 0 : setFallbackIntervalPattern(pattern, status);
430 : }
431 0 : ures_close(itvDtPtnResource);
432 0 : ures_close(calTypeBundle);
433 :
434 :
435 : // Instantiate the sink
436 0 : DateIntervalSink sink(*this, calendarTypeToUse);
437 0 : const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
438 :
439 : // Already loaded calendar types
440 0 : Hashtable loadedCalendarTypes(FALSE, status);
441 :
442 0 : if (U_SUCCESS(status)) {
443 0 : while (!calendarTypeToUseUString.isBogus()) {
444 : // Set an error when a loop is detected
445 0 : if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
446 0 : status = U_INVALID_FORMAT_ERROR;
447 0 : break;
448 : }
449 :
450 : // Register the calendar type to avoid loops
451 0 : loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
452 0 : if (U_FAILURE(status)) { break; }
453 :
454 : // Get the calendar string
455 0 : CharString calTypeBuffer;
456 0 : calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
457 0 : if (U_FAILURE(status)) { break; }
458 0 : const char *calType = calTypeBuffer.data();
459 :
460 : // Reset the next calendar type to load.
461 0 : sink.resetNextCalendarType();
462 :
463 : // Get all resources for this calendar type
464 0 : ures_getAllItemsWithFallback(calBundle, calType, sink, status);
465 : }
466 : }
467 : }
468 :
469 : // Close the opened resource bundles
470 0 : ures_close(calBundle);
471 0 : ures_close(rb);
472 : }
473 :
474 : void
475 0 : DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
476 : UCalendarDateFields lrgDiffCalUnit,
477 : const UnicodeString& intervalPattern,
478 : UErrorCode& status) {
479 0 : IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
480 0 : if ( U_FAILURE(status) ) {
481 0 : return;
482 : }
483 0 : UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
484 0 : UBool emptyHash = false;
485 0 : if ( patternsOfOneSkeleton == NULL ) {
486 0 : patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
487 0 : emptyHash = true;
488 : }
489 :
490 0 : patternsOfOneSkeleton[index] = intervalPattern;
491 0 : if ( emptyHash == TRUE ) {
492 0 : fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
493 : }
494 : }
495 :
496 :
497 :
498 : void
499 0 : DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
500 : int32_t* skeletonFieldWidth) {
501 0 : const int8_t PATTERN_CHAR_BASE = 0x41;
502 : int32_t i;
503 0 : for ( i = 0; i < skeleton.length(); ++i ) {
504 : // it is an ASCII char in skeleton
505 0 : int8_t ch = (int8_t)skeleton.charAt(i);
506 0 : ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
507 : }
508 0 : }
509 :
510 :
511 :
512 : UBool
513 0 : DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
514 : char patternLetter) {
515 0 : if ( patternLetter == 'M' ) {
516 0 : if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
517 0 : (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
518 0 : return true;
519 : }
520 : }
521 0 : return false;
522 : }
523 :
524 :
525 :
526 : const UnicodeString*
527 0 : 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 0 : };
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 0 : };
562 :
563 0 : const int32_t DIFFERENT_FIELD = 0x1000;
564 0 : const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
565 0 : const int32_t BASE = 0x41;
566 0 : const UChar CHAR_V = 0x0076;
567 0 : 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 0 : UBool replaceZWithV = false;
573 0 : const UnicodeString* inputSkeleton = &skeleton;
574 0 : UnicodeString copySkeleton;
575 0 : if ( skeleton.indexOf(CHAR_Z) != -1 ) {
576 0 : copySkeleton = skeleton;
577 0 : copySkeleton.findAndReplace(UnicodeString(CHAR_Z), UnicodeString(CHAR_V));
578 0 : inputSkeleton = ©Skeleton;
579 0 : replaceZWithV = true;
580 : }
581 :
582 0 : parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
583 0 : int32_t bestDistance = MAX_POSITIVE_INT;
584 0 : 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 0 : bestMatchDistanceInfo = 0;
591 0 : int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
592 :
593 0 : int32_t pos = UHASH_FIRST;
594 0 : const UHashElement* elem = NULL;
595 0 : while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) {
596 0 : const UHashTok keyTok = elem->key;
597 0 : UnicodeString* skeleton = (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 0 : for ( i = 0; i < fieldLength; ++i ) {
607 0 : skeletonFieldWidth[i] = 0;
608 : }
609 0 : parseSkeleton(*skeleton, skeletonFieldWidth);
610 : // calculate distance
611 0 : int32_t distance = 0;
612 0 : int8_t fieldDifference = 1;
613 0 : for ( i = 0; i < fieldLength; ++i ) {
614 0 : int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
615 0 : int32_t fieldWidth = skeletonFieldWidth[i];
616 0 : if ( inputFieldWidth == fieldWidth ) {
617 0 : continue;
618 : }
619 0 : if ( inputFieldWidth == 0 ) {
620 0 : fieldDifference = -1;
621 0 : distance += DIFFERENT_FIELD;
622 0 : } else if ( fieldWidth == 0 ) {
623 0 : fieldDifference = -1;
624 0 : distance += DIFFERENT_FIELD;
625 0 : } else if (stringNumeric(inputFieldWidth, fieldWidth,
626 0 : (char)(i+BASE) ) ) {
627 0 : distance += STRING_NUMERIC_DIFFERENCE;
628 : } else {
629 0 : distance += (inputFieldWidth > fieldWidth) ?
630 : (inputFieldWidth - fieldWidth) :
631 : (fieldWidth - inputFieldWidth);
632 : }
633 : }
634 0 : if ( distance < bestDistance ) {
635 0 : bestSkeleton = skeleton;
636 0 : bestDistance = distance;
637 0 : bestMatchDistanceInfo = fieldDifference;
638 : }
639 0 : if ( distance == 0 ) {
640 0 : bestMatchDistanceInfo = 0;
641 0 : break;
642 : }
643 : }
644 0 : if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
645 0 : bestMatchDistanceInfo = 2;
646 : }
647 0 : return bestSkeleton;
648 : }
649 :
650 :
651 :
652 : DateIntervalInfo::IntervalPatternIndex
653 0 : DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
654 : UErrorCode& status) {
655 0 : if ( U_FAILURE(status) ) {
656 0 : return kIPI_MAX_INDEX;
657 : }
658 0 : IntervalPatternIndex index = kIPI_MAX_INDEX;
659 0 : switch ( field ) {
660 : case UCAL_ERA:
661 0 : index = kIPI_ERA;
662 0 : break;
663 : case UCAL_YEAR:
664 0 : index = kIPI_YEAR;
665 0 : break;
666 : case UCAL_MONTH:
667 0 : index = kIPI_MONTH;
668 0 : break;
669 : case UCAL_DATE:
670 : case UCAL_DAY_OF_WEEK:
671 : //case UCAL_DAY_OF_MONTH:
672 0 : index = kIPI_DATE;
673 0 : break;
674 : case UCAL_AM_PM:
675 0 : index = kIPI_AM_PM;
676 0 : break;
677 : case UCAL_HOUR:
678 : case UCAL_HOUR_OF_DAY:
679 0 : index = kIPI_HOUR;
680 0 : break;
681 : case UCAL_MINUTE:
682 0 : index = kIPI_MINUTE;
683 0 : break;
684 : case UCAL_SECOND:
685 0 : index = kIPI_SECOND;
686 0 : break;
687 : default:
688 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
689 : }
690 0 : return index;
691 : }
692 :
693 :
694 :
695 : void
696 0 : DateIntervalInfo::deleteHash(Hashtable* hTable)
697 : {
698 0 : if ( hTable == NULL ) {
699 0 : return;
700 : }
701 0 : int32_t pos = UHASH_FIRST;
702 0 : const UHashElement* element = NULL;
703 0 : while ( (element = hTable->nextElement(pos)) != NULL ) {
704 0 : const UHashTok valueTok = element->value;
705 0 : const UnicodeString* value = (UnicodeString*)valueTok.pointer;
706 0 : delete[] value;
707 : }
708 0 : 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
724 0 : U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
725 0 : const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
726 0 : const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
727 0 : UBool ret = TRUE;
728 : int8_t i;
729 0 : for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) {
730 0 : ret = (pattern1[i] == pattern2[i]);
731 : }
732 0 : return ret;
733 : }
734 :
735 : U_CDECL_END
736 :
737 :
738 : Hashtable*
739 0 : DateIntervalInfo::initHash(UErrorCode& status) {
740 0 : if ( U_FAILURE(status) ) {
741 0 : return NULL;
742 : }
743 : Hashtable* hTable;
744 0 : if ( (hTable = new Hashtable(FALSE, status)) == NULL ) {
745 0 : status = U_MEMORY_ALLOCATION_ERROR;
746 0 : return NULL;
747 : }
748 0 : if ( U_FAILURE(status) ) {
749 0 : delete hTable;
750 0 : return NULL;
751 : }
752 0 : hTable->setValueComparator(dtitvinfHashTableValueComparator);
753 0 : return hTable;
754 : }
755 :
756 :
757 : void
758 0 : DateIntervalInfo::copyHash(const Hashtable* source,
759 : Hashtable* target,
760 : UErrorCode& status) {
761 0 : if ( U_FAILURE(status) ) {
762 0 : return;
763 : }
764 0 : int32_t pos = UHASH_FIRST;
765 0 : const UHashElement* element = NULL;
766 0 : if ( source ) {
767 0 : while ( (element = source->nextElement(pos)) != NULL ) {
768 0 : const UHashTok keyTok = element->key;
769 0 : const UnicodeString* key = (UnicodeString*)keyTok.pointer;
770 0 : const UHashTok valueTok = element->value;
771 0 : const UnicodeString* value = (UnicodeString*)valueTok.pointer;
772 0 : UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
773 : int8_t i;
774 0 : for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
775 0 : copy[i] = value[i];
776 : }
777 0 : target->put(UnicodeString(*key), copy, status);
778 0 : if ( U_FAILURE(status) ) {
779 0 : return;
780 : }
781 : }
782 : }
783 : }
784 :
785 :
786 : U_NAMESPACE_END
787 :
788 : #endif
|