2 *******************************************************************************
3 * Copyright (C) 2007-2016, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
9 *******************************************************************************
12 #include "unicode/utypes.h"
13 #if !UCONFIG_NO_FORMATTING
15 #include "unicode/datefmt.h"
16 #include "unicode/decimfmt.h"
17 #include "unicode/dtfmtsym.h"
18 #include "unicode/dtptngen.h"
19 #include "unicode/simpleformatter.h"
20 #include "unicode/smpdtfmt.h"
21 #include "unicode/udat.h"
22 #include "unicode/udatpg.h"
23 #include "unicode/uniset.h"
24 #include "unicode/uloc.h"
25 #include "unicode/ures.h"
26 #include "unicode/ustring.h"
27 #include "unicode/rep.h"
38 #include "dtptngen_impl.h"
42 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
44 * If we are on EBCDIC, use an iterator which will
45 * traverse the bundles in ASCII order.
47 #define U_USE_ASCII_BUNDLE_ITERATOR
48 #define U_SORT_ASCII_BUNDLE_ITERATOR
51 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
53 #include "unicode/ustring.h"
58 UResourceBundle
*item
;
61 struct UResourceBundleAIterator
{
62 UResourceBundle
*bund
;
68 /* Must be C linkage to pass function pointer to the sort function */
72 static int32_t U_CALLCONV
73 ures_a_codepointSort(const void *context
, const void *left
, const void *right
) {
74 //CompareContext *cmp=(CompareContext *)context;
75 return u_strcmp(((const UResAEntry
*)left
)->key
,
76 ((const UResAEntry
*)right
)->key
);
81 static void ures_a_open(UResourceBundleAIterator
*aiter
, UResourceBundle
*bund
, UErrorCode
*status
) {
82 if(U_FAILURE(*status
)) {
86 aiter
->num
= ures_getSize(aiter
->bund
);
88 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
89 aiter
->entries
= NULL
;
91 aiter
->entries
= (UResAEntry
*)uprv_malloc(sizeof(UResAEntry
)*aiter
->num
);
92 for(int i
=0;i
<aiter
->num
;i
++) {
93 aiter
->entries
[i
].item
= ures_getByIndex(aiter
->bund
, i
, NULL
, status
);
94 const char *akey
= ures_getKey(aiter
->entries
[i
].item
);
95 int32_t len
= uprv_strlen(akey
)+1;
96 aiter
->entries
[i
].key
= (UChar
*)uprv_malloc(len
*sizeof(UChar
));
97 u_charsToUChars(akey
, aiter
->entries
[i
].key
, len
);
99 uprv_sortArray(aiter
->entries
, aiter
->num
, sizeof(UResAEntry
), ures_a_codepointSort
, NULL
, TRUE
, status
);
103 static void ures_a_close(UResourceBundleAIterator
*aiter
) {
104 #if defined(U_SORT_ASCII_BUNDLE_ITERATOR)
105 for(int i
=0;i
<aiter
->num
;i
++) {
106 uprv_free(aiter
->entries
[i
].key
);
107 ures_close(aiter
->entries
[i
].item
);
112 static const UChar
*ures_a_getNextString(UResourceBundleAIterator
*aiter
, int32_t *len
, const char **key
, UErrorCode
*err
) {
113 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
114 return ures_getNextString(aiter
->bund
, len
, key
, err
);
116 if(U_FAILURE(*err
)) return NULL
;
117 UResourceBundle
*item
= aiter
->entries
[aiter
->cursor
].item
;
118 const UChar
* ret
= ures_getString(item
, len
, err
);
119 *key
= ures_getKey(item
);
131 // *****************************************************************************
132 // class DateTimePatternGenerator
133 // *****************************************************************************
134 static const UChar Canonical_Items
[] = {
136 CAP_G
, LOW_Y
, CAP_Q
, CAP_M
, LOW_W
, CAP_W
, CAP_E
, LOW_D
, CAP_D
, CAP_F
,
137 CAP_H
, LOW_M
, LOW_S
, CAP_S
, LOW_V
, 0
140 static const dtTypeElem dtTypes
[] = {
141 // patternChar, field, type, minLen, weight
142 {CAP_G
, UDATPG_ERA_FIELD
, DT_SHORT
, 1, 3,},
143 {CAP_G
, UDATPG_ERA_FIELD
, DT_LONG
, 4, 0},
144 {LOW_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
, 1, 20},
145 {CAP_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20},
146 {LOW_U
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 20},
147 {LOW_R
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 3*DT_DELTA
, 1, 20},
148 {CAP_U
, UDATPG_YEAR_FIELD
, DT_SHORT
, 1, 3},
149 {CAP_U
, UDATPG_YEAR_FIELD
, DT_LONG
, 4, 0},
150 {CAP_U
, UDATPG_YEAR_FIELD
, DT_NARROW
, 5, 0},
151 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
, 1, 2},
152 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
, 3, 0},
153 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
, 4, 0},
154 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
155 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
+ DT_DELTA
, 3, 0},
156 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
+ DT_DELTA
, 4, 0},
157 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NUMERIC
, 1, 2},
158 {CAP_M
, UDATPG_MONTH_FIELD
, DT_SHORT
, 3, 0},
159 {CAP_M
, UDATPG_MONTH_FIELD
, DT_LONG
, 4, 0},
160 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NARROW
, 5, 0},
161 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
162 {CAP_L
, UDATPG_MONTH_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
163 {CAP_L
, UDATPG_MONTH_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
164 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
165 {LOW_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1},
166 {LOW_W
, UDATPG_WEEK_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 2},
167 {CAP_W
, UDATPG_WEEK_OF_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 0},
168 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
, 1, 3},
169 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
, 4, 0},
170 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
, 5, 0},
171 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 2},
172 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- 2*DT_DELTA
, 3, 0},
173 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
174 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- 2*DT_DELTA
, 5, 0},
175 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical
176 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
177 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
178 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
179 {LOW_D
, UDATPG_DAY_FIELD
, DT_NUMERIC
, 1, 2},
180 {CAP_D
, UDATPG_DAY_OF_YEAR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 3},
181 {CAP_F
, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 0},
182 {LOW_G
, UDATPG_DAY_FIELD
, DT_NUMERIC
+ 3*DT_DELTA
, 1, 20}, // really internal use, so we don't care
183 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
, 1, 0},
184 {CAP_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 10*DT_DELTA
, 1, 2}, // 24 hour
185 {LOW_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 11*DT_DELTA
, 1, 2}, // 24 hour
186 {LOW_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
, 1, 2}, // 12 hour
187 {CAP_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // 12 hour
188 {LOW_M
, UDATPG_MINUTE_FIELD
, DT_NUMERIC
, 1, 2},
189 {LOW_S
, UDATPG_SECOND_FIELD
, DT_NUMERIC
, 1, 2},
190 {CAP_S
, UDATPG_FRACTIONAL_SECOND_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1000},
191 {CAP_A
, UDATPG_SECOND_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 1000},
192 {LOW_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- 2*DT_DELTA
, 1, 0},
193 {LOW_V
, UDATPG_ZONE_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
194 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
, 1, 3},
195 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_LONG
, 4, 0},
196 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 3},
197 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
198 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 5, 0},
199 {CAP_O
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
200 {CAP_O
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
201 {CAP_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
202 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 2, 0},
203 {CAP_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
204 {CAP_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
205 {CAP_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
206 {LOW_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
207 {LOW_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
208 {LOW_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
209 {LOW_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
, 1, 2}, // 12/24 hour
210 {CAP_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
, 1, 2}, // 12/24 hour no AM/PM
211 {0, UDATPG_FIELD_COUNT
, 0, 0, 0} , // last row of dtTypes[]
214 static const char* const CLDR_FIELD_APPEND
[] = {
215 "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week", "Day", "*", "*", "*",
216 "Hour", "Minute", "Second", "*", "Timezone"
219 static const char* const CLDR_FIELD_NAME
[] = {
220 "era", "year", "quarter", "month", "week", "*", "weekday", "*", "*", "day", "dayperiod",
221 "hour", "minute", "second", "*", "zone"
224 static const char* const Resource_Fields
[] = {
225 "day", "dayperiod", "era", "hour", "minute", "month", "second", "week",
226 "weekday", "year", "zone", "quarter" };
229 static const UChar UDATPG_ItemFormat
[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
230 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524
232 //static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
234 static const char DT_DateTimePatternsTag
[]="DateTimePatterns";
235 static const char DT_DateTimeCalendarTag
[]="calendar";
236 static const char DT_DateTimeGregorianTag
[]="gregorian";
237 static const char DT_DateTimeAppendItemsTag
[]="appendItems";
238 static const char DT_DateTimeFieldsTag
[]="fields";
239 static const char DT_DateTimeAvailableFormatsTag
[]="availableFormats";
240 //static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);
242 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator
)
243 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration
)
244 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration
)
246 DateTimePatternGenerator
* U_EXPORT2
247 DateTimePatternGenerator::createInstance(UErrorCode
& status
) {
248 return createInstance(Locale::getDefault(), status
);
251 DateTimePatternGenerator
* U_EXPORT2
252 DateTimePatternGenerator::createInstance(const Locale
& locale
, UErrorCode
& status
) {
253 if (U_FAILURE(status
)) {
256 LocalPointer
<DateTimePatternGenerator
> result(
257 new DateTimePatternGenerator(locale
, status
), status
);
258 return U_SUCCESS(status
) ? result
.orphan() : NULL
;
261 DateTimePatternGenerator
* U_EXPORT2
262 DateTimePatternGenerator::createEmptyInstance(UErrorCode
& status
) {
263 DateTimePatternGenerator
*result
= new DateTimePatternGenerator(status
);
264 if (result
== NULL
) {
265 status
= U_MEMORY_ALLOCATION_ERROR
;
267 if (U_FAILURE(status
)) {
274 DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode
&status
) :
276 fAvailableFormatKeyHash(NULL
)
278 fp
= new FormatParser();
279 dtMatcher
= new DateTimeMatcher();
280 distanceInfo
= new DistanceInfo();
281 patternMap
= new PatternMap();
282 if (fp
== NULL
|| dtMatcher
== NULL
|| distanceInfo
== NULL
|| patternMap
== NULL
) {
283 status
= U_MEMORY_ALLOCATION_ERROR
;
287 DateTimePatternGenerator::DateTimePatternGenerator(const Locale
& locale
, UErrorCode
&status
) :
289 fAvailableFormatKeyHash(NULL
)
291 fp
= new FormatParser();
292 dtMatcher
= new DateTimeMatcher();
293 distanceInfo
= new DistanceInfo();
294 patternMap
= new PatternMap();
295 if (fp
== NULL
|| dtMatcher
== NULL
|| distanceInfo
== NULL
|| patternMap
== NULL
) {
296 status
= U_MEMORY_ALLOCATION_ERROR
;
299 initData(locale
, status
);
303 DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator
& other
) :
306 fAvailableFormatKeyHash(NULL
)
308 fp
= new FormatParser();
309 dtMatcher
= new DateTimeMatcher();
310 distanceInfo
= new DistanceInfo();
311 patternMap
= new PatternMap();
315 DateTimePatternGenerator
&
316 DateTimePatternGenerator::operator=(const DateTimePatternGenerator
& other
) {
318 if (&other
== this) {
321 pLocale
= other
.pLocale
;
322 fDefaultHourFormatChar
= other
.fDefaultHourFormatChar
;
324 dtMatcher
->copyFrom(other
.dtMatcher
->skeleton
);
325 *distanceInfo
= *(other
.distanceInfo
);
326 dateTimeFormat
= other
.dateTimeFormat
;
327 decimal
= other
.decimal
;
328 // NUL-terminate for the C API.
329 dateTimeFormat
.getTerminatedBuffer();
330 decimal
.getTerminatedBuffer();
332 if ( other
.skipMatcher
== NULL
) {
336 skipMatcher
= new DateTimeMatcher(*other
.skipMatcher
);
338 for (int32_t i
=0; i
< UDATPG_FIELD_COUNT
; ++i
) {
339 appendItemFormats
[i
] = other
.appendItemFormats
[i
];
340 appendItemNames
[i
] = other
.appendItemNames
[i
];
341 // NUL-terminate for the C API.
342 appendItemFormats
[i
].getTerminatedBuffer();
343 appendItemNames
[i
].getTerminatedBuffer();
345 UErrorCode status
= U_ZERO_ERROR
;
346 patternMap
->copyFrom(*other
.patternMap
, status
);
347 copyHashtable(other
.fAvailableFormatKeyHash
, status
);
353 DateTimePatternGenerator::operator==(const DateTimePatternGenerator
& other
) const {
354 if (this == &other
) {
357 if ((pLocale
==other
.pLocale
) && (patternMap
->equals(*other
.patternMap
)) &&
358 (dateTimeFormat
==other
.dateTimeFormat
) && (decimal
==other
.decimal
)) {
359 for ( int32_t i
=0 ; i
<UDATPG_FIELD_COUNT
; ++i
) {
360 if ((appendItemFormats
[i
] != other
.appendItemFormats
[i
]) ||
361 (appendItemNames
[i
] != other
.appendItemNames
[i
]) ) {
373 DateTimePatternGenerator::operator!=(const DateTimePatternGenerator
& other
) const {
374 return !operator==(other
);
377 DateTimePatternGenerator::~DateTimePatternGenerator() {
378 if (fAvailableFormatKeyHash
!=NULL
) {
379 delete fAvailableFormatKeyHash
;
382 if (fp
!= NULL
) delete fp
;
383 if (dtMatcher
!= NULL
) delete dtMatcher
;
384 if (distanceInfo
!= NULL
) delete distanceInfo
;
385 if (patternMap
!= NULL
) delete patternMap
;
386 if (skipMatcher
!= NULL
) delete skipMatcher
;
391 UInitOnce initOnce
= U_INITONCE_INITIALIZER
;
392 UHashtable
*localeToAllowedHourFormatsMap
= NULL
;
394 // Value deleter for hashmap.
395 void deleteAllowedHourFormats(void *ptr
) {
399 // Close hashmap at cleanup.
400 UBool
allowedHourFormatsCleanup() {
401 uhash_close(localeToAllowedHourFormatsMap
);
405 enum AllowedHourFormat
{
406 ALLOWED_HOUR_FORMAT_UNKNOWN
= -1,
407 ALLOWED_HOUR_FORMAT_h
,
408 ALLOWED_HOUR_FORMAT_H
,
409 ALLOWED_HOUR_FORMAT_hb
,
410 ALLOWED_HOUR_FORMAT_Hb
,
411 ALLOWED_HOUR_FORMAT_hB
,
412 ALLOWED_HOUR_FORMAT_HB
418 DateTimePatternGenerator::initData(const Locale
& locale
, UErrorCode
&status
) {
419 //const char *baseLangName = locale.getBaseName(); // unused
422 fAvailableFormatKeyHash
=NULL
;
424 addICUPatterns(locale
, status
);
425 if (U_FAILURE(status
)) {
428 addCLDRData(locale
, status
);
429 setDateTimeFromCalendar(locale
, status
);
430 setDecimalSymbols(locale
, status
);
431 umtx_initOnce(initOnce
, loadAllowedHourFormatsData
, status
);
432 getAllowedHourFormats(locale
, status
);
433 } // DateTimePatternGenerator::initData
437 struct AllowedHourFormatsSink
: public ResourceTableSink
{
438 // Initialize sub-sinks.
439 AllowedHourFormatsSink() : localeSink(*this), allowedListSink(*this) {}
440 virtual ~AllowedHourFormatsSink();
443 virtual ResourceTableSink
*getOrCreateTableSink(const char *key
, int32_t, UErrorCode
&status
) {
444 if (U_FAILURE(status
)) { return NULL
; }
450 struct LocaleSink
: public ResourceTableSink
{
451 AllowedHourFormatsSink
&outer
;
452 LocaleSink(AllowedHourFormatsSink
&outer
) : outer(outer
) {}
453 virtual ~LocaleSink();
455 virtual void put(const char *key
, const ResourceValue
&value
, UErrorCode
&status
) {
456 if (U_FAILURE(status
)) { return; }
458 if (uprv_strcmp(key
, "allowed") == 0) {
459 outer
.allowedFormats
= static_cast<int32_t *>(uprv_malloc(2 * sizeof(int32_t)));
460 outer
.allowedFormatsLength
= 1;
461 if (outer
.allowedFormats
== NULL
) {
462 status
= U_MEMORY_ALLOCATION_ERROR
;
465 outer
.allowedFormats
[0] = outer
.getHourFormatFromUnicodeString(
466 value
.getUnicodeString(status
));
470 virtual ResourceArraySink
*getOrCreateArraySink(const char *key
, int32_t size
, UErrorCode
&status
) {
471 if (U_FAILURE(status
)) { return NULL
; }
473 if (uprv_strcmp(key
, "allowed") == 0) {
474 outer
.allowedFormats
= static_cast<int32_t *>(uprv_malloc((size
+ 1) * sizeof(int32_t)));
475 outer
.allowedFormatsLength
= size
;
476 if (outer
.allowedFormats
== NULL
) {
477 status
= U_MEMORY_ALLOCATION_ERROR
;
480 return &outer
.allowedListSink
;
486 virtual void leave(UErrorCode
&status
) {
487 if (U_FAILURE(status
) || outer
.allowedFormats
== NULL
) { return; }
489 outer
.allowedFormats
[outer
.allowedFormatsLength
] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
490 uhash_put(localeToAllowedHourFormatsMap
, const_cast<char *>(outer
.locale
), outer
.allowedFormats
, &status
);
491 outer
.allowedFormats
= NULL
;
495 struct AllowedListSink
: public ResourceArraySink
{
496 AllowedHourFormatsSink
&outer
;
497 AllowedListSink(AllowedHourFormatsSink
&outer
) : outer(outer
) {}
498 virtual ~AllowedListSink();
500 virtual void put(int32_t index
, const ResourceValue
&value
, UErrorCode
&status
) {
501 if (U_FAILURE(status
)) { return; }
503 outer
.allowedFormats
[index
] = outer
.getHourFormatFromUnicodeString(
504 value
.getUnicodeString(status
));
509 int32_t *allowedFormats
;
510 int32_t allowedFormatsLength
;
512 AllowedHourFormat
getHourFormatFromUnicodeString(UnicodeString s
) {
513 if (s
.length() == 1) {
514 if (s
[0] == LOW_H
) { return ALLOWED_HOUR_FORMAT_h
; }
515 if (s
[0] == CAP_H
) { return ALLOWED_HOUR_FORMAT_H
; }
516 } else if (s
.length() == 2) {
517 if (s
[0] == LOW_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_hb
; }
518 if (s
[0] == CAP_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_Hb
; }
519 if (s
[0] == LOW_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_hB
; }
520 if (s
[0] == CAP_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_HB
; }
523 return ALLOWED_HOUR_FORMAT_UNKNOWN
;
529 AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
530 AllowedHourFormatsSink::LocaleSink::~LocaleSink() {}
531 AllowedHourFormatsSink::AllowedListSink::~AllowedListSink() {}
533 void DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode
&status
) {
534 if (U_FAILURE(status
)) { return; }
535 localeToAllowedHourFormatsMap
= uhash_open(
536 uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
537 uhash_setValueDeleter(localeToAllowedHourFormatsMap
, deleteAllowedHourFormats
);
538 LocalUResourceBundlePointer
rb(ures_openDirect(NULL
, "supplementalData", &status
));
540 AllowedHourFormatsSink sink
;
541 // TODO: Currently in the enumeration each table allocates a new array.
542 // Try to reduce the number of memory allocations. Consider storing a
543 // UVector32 with the concatenation of all of the sub-arrays, put the start index
544 // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
545 // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
546 // object. Remember to clean up the vector, too.
547 ures_getAllTableItemsWithFallback(rb
.getAlias(), "timeData", sink
, status
);
549 ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS
, allowedHourFormatsCleanup
);
552 void DateTimePatternGenerator::getAllowedHourFormats(const Locale
&locale
, UErrorCode
&status
) {
553 if (U_FAILURE(status
)) { return; }
555 const char *localeID
= locale
.getName();
556 char maxLocaleID
[ULOC_FULLNAME_CAPACITY
];
557 int32_t length
= uloc_addLikelySubtags(localeID
, maxLocaleID
, ULOC_FULLNAME_CAPACITY
, &status
);
558 if (U_FAILURE(status
)) {
560 } else if (length
== ULOC_FULLNAME_CAPACITY
) { // no room for NUL
561 status
= U_BUFFER_OVERFLOW_ERROR
;
564 Locale maxLocale
= Locale(maxLocaleID
);
566 const char *country
= maxLocale
.getCountry();
567 if (*country
== '\0') { country
= "001"; }
568 const char *language
= maxLocale
.getLanguage();
570 CharString langCountry
;
571 langCountry
.append(language
, uprv_strlen(language
), status
);
572 langCountry
.append('_', status
);
573 langCountry
.append(country
, uprv_strlen(country
), status
);
575 int32_t *allowedFormats
;
576 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, langCountry
.data());
577 if (allowedFormats
== NULL
) {
578 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, const_cast<char *>(country
));
581 if (allowedFormats
!= NULL
) { // Lookup is successful
582 for (int32_t i
= 0; i
< UPRV_LENGTHOF(fAllowedHourFormats
); ++i
) {
583 fAllowedHourFormats
[i
] = allowedFormats
[i
];
584 if (allowedFormats
[i
] == ALLOWED_HOUR_FORMAT_UNKNOWN
) {
588 } else { // Lookup failed, twice
589 fAllowedHourFormats
[0] = ALLOWED_HOUR_FORMAT_H
;
590 fAllowedHourFormats
[1] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
595 DateTimePatternGenerator::getSkeleton(const UnicodeString
& pattern
, UErrorCode
&
598 DateTimeMatcher matcher
;
599 PtnSkeleton localSkeleton
;
600 matcher
.set(pattern
, &fp
, localSkeleton
);
601 return localSkeleton
.getSkeleton();
605 DateTimePatternGenerator::staticGetSkeleton(
606 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
608 DateTimeMatcher matcher
;
609 PtnSkeleton localSkeleton
;
610 matcher
.set(pattern
, &fp
, localSkeleton
);
611 return localSkeleton
.getSkeleton();
615 DateTimePatternGenerator::getBaseSkeleton(const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
617 DateTimeMatcher matcher
;
618 PtnSkeleton localSkeleton
;
619 matcher
.set(pattern
, &fp
, localSkeleton
);
620 return localSkeleton
.getBaseSkeleton();
624 DateTimePatternGenerator::staticGetBaseSkeleton(
625 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
627 DateTimeMatcher matcher
;
628 PtnSkeleton localSkeleton
;
629 matcher
.set(pattern
, &fp
, localSkeleton
);
630 return localSkeleton
.getBaseSkeleton();
634 DateTimePatternGenerator::addICUPatterns(const Locale
& locale
, UErrorCode
& status
) {
635 UnicodeString dfPattern
;
636 UnicodeString conflictingString
;
639 if (U_FAILURE(status
)) {
643 // Load with ICU patterns
644 for (int32_t i
=DateFormat::kFull
; i
<=DateFormat::kShort
; i
++) {
645 DateFormat::EStyle style
= (DateFormat::EStyle
)i
;
646 df
= DateFormat::createDateInstance(style
, locale
);
647 SimpleDateFormat
* sdf
;
648 if (df
!= NULL
&& (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != NULL
) {
649 addPattern(sdf
->toPattern(dfPattern
), FALSE
, conflictingString
, status
);
651 // TODO Maybe we should return an error when the date format isn't simple.
653 if (U_FAILURE(status
)) {
657 df
= DateFormat::createTimeInstance(style
, locale
);
658 if (df
!= NULL
&& (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != NULL
) {
659 addPattern(sdf
->toPattern(dfPattern
), FALSE
, conflictingString
, status
);
661 if ( i
==DateFormat::kMedium
) {
662 hackPattern
= dfPattern
;
665 // TODO Maybe we should return an error when the date format isn't simple.
667 if (U_FAILURE(status
)) {
674 DateTimePatternGenerator::hackTimes(const UnicodeString
& hackPattern
, UErrorCode
& status
) {
675 UnicodeString conflictingString
;
677 fp
->set(hackPattern
);
680 for (int32_t i
=0; i
<fp
->itemNumber
; ++i
) {
681 UnicodeString field
= fp
->items
[i
];
682 if ( fp
->isQuoteLiteral(field
) ) {
684 UnicodeString quoteLiteral
;
685 fp
->getQuoteLiteral(quoteLiteral
, &i
);
686 mmss
+= quoteLiteral
;
690 if (fp
->isPatternSeparator(field
) && gotMm
) {
694 UChar ch
=field
.charAt(0);
705 addPattern(mmss
, FALSE
, conflictingString
, status
);
709 if (gotMm
|| ch
==LOW_Z
|| ch
==CAP_Z
|| ch
==LOW_V
|| ch
==CAP_V
) {
719 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
721 static const UChar hourFormatChars
[] = { CAP_H
, LOW_H
, CAP_K
, LOW_K
, 0 }; // HhKk, the hour format characters
724 DateTimePatternGenerator::addCLDRData(const Locale
& locale
, UErrorCode
& err
) {
725 UResourceBundle
*rb
, *calTypeBundle
, *calBundle
;
726 UResourceBundle
*patBundle
, *fieldBundle
, *fBundle
;
727 UnicodeString rbPattern
, value
, field
;
728 UnicodeString conflictingPattern
;
729 const char *key
=NULL
;
732 UnicodeString
defaultItemFormat(TRUE
, UDATPG_ItemFormat
, UPRV_LENGTHOF(UDATPG_ItemFormat
)-1); // Read-only alias.
736 fDefaultHourFormatChar
= 0;
737 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
738 appendItemNames
[i
]=CAP_F
;
740 appendItemNames
[i
]+=(UChar
)(i
+0x30);
743 appendItemNames
[i
]+=(UChar
)0x31;
744 appendItemNames
[i
]+=(UChar
)(i
-10 + 0x30);
746 // NUL-terminate for the C API.
747 appendItemNames
[i
].getTerminatedBuffer();
750 rb
= ures_open(NULL
, locale
.getName(), &err
);
751 if (rb
== NULL
|| U_FAILURE(err
)) {
754 const char *curLocaleName
=ures_getLocaleByType(rb
, ULOC_ACTUAL_LOCALE
, &err
);
755 const char * calendarTypeToUse
= DT_DateTimeGregorianTag
; // initial default
756 char calendarType
[ULOC_KEYWORDS_CAPACITY
]; // to be filled in with the type to use, if all goes well
757 if ( U_SUCCESS(err
) ) {
758 char localeWithCalendarKey
[ULOC_LOCALE_IDENTIFIER_CAPACITY
];
759 // obtain a locale that always has the calendar key value that should be used
760 (void)ures_getFunctionalEquivalent(localeWithCalendarKey
, ULOC_LOCALE_IDENTIFIER_CAPACITY
, NULL
,
761 "calendar", "calendar", locale
.getName(), NULL
, FALSE
, &err
);
762 localeWithCalendarKey
[ULOC_LOCALE_IDENTIFIER_CAPACITY
-1] = 0; // ensure null termination
763 // now get the calendar key value from that locale
764 int32_t calendarTypeLen
= uloc_getKeywordValue(localeWithCalendarKey
, "calendar", calendarType
, ULOC_KEYWORDS_CAPACITY
, &err
);
765 if (U_SUCCESS(err
) && calendarTypeLen
< ULOC_KEYWORDS_CAPACITY
) {
766 calendarTypeToUse
= calendarType
;
770 calBundle
= ures_getByKeyWithFallback(rb
, DT_DateTimeCalendarTag
, NULL
, &err
);
771 calTypeBundle
= ures_getByKeyWithFallback(calBundle
, calendarTypeToUse
, NULL
, &err
);
775 patBundle
= ures_getByKeyWithFallback(calTypeBundle
, DT_DateTimePatternsTag
, NULL
, &err
);
776 while (U_SUCCESS(err
)) {
777 rbPattern
= ures_getNextUnicodeString(patBundle
, &key
, &err
);
779 if (rbPattern
.length()==0 ) {
780 break; // no more pattern
784 setDateTimeFormat(rbPattern
);
785 } else if (dtCount
==4) { // short time format
786 // set fDefaultHourFormatChar to the hour format character from this pattern
787 int32_t tfIdx
, tfLen
= rbPattern
.length();
788 UBool ignoreChars
= FALSE
;
789 for (tfIdx
= 0; tfIdx
< tfLen
; tfIdx
++) {
790 UChar tfChar
= rbPattern
.charAt(tfIdx
);
791 if ( tfChar
== SINGLE_QUOTE
) {
792 ignoreChars
= !ignoreChars
; // toggle (handle quoted literals & '' for single quote)
793 } else if ( !ignoreChars
&& u_strchr(hourFormatChars
, tfChar
) != NULL
) {
794 fDefaultHourFormatChar
= tfChar
;
801 ures_close(patBundle
);
804 patBundle
= ures_getByKeyWithFallback(calTypeBundle
, DT_DateTimeAppendItemsTag
, NULL
, &err
);
806 UnicodeString itemKey
;
807 while (U_SUCCESS(err
)) {
808 rbPattern
= ures_getNextUnicodeString(patBundle
, &key
, &err
);
809 if (rbPattern
.length()==0 ) {
810 break; // no more pattern
813 setAppendItemFormat(getAppendFormatNumber(key
), rbPattern
);
816 ures_close(patBundle
);
820 fBundle
= ures_getByKeyWithFallback(rb
, DT_DateTimeFieldsTag
, NULL
, &err
);
821 for (i
=0; i
<MAX_RESOURCE_FIELD
; ++i
) {
823 patBundle
= ures_getByKeyWithFallback(fBundle
, Resource_Fields
[i
], NULL
, &err
);
824 fieldBundle
= ures_getByKeyWithFallback(patBundle
, "dn", NULL
, &err
);
825 rbPattern
= ures_getNextUnicodeString(fieldBundle
, &key
, &err
);
826 ures_close(fieldBundle
);
827 ures_close(patBundle
);
828 if (rbPattern
.length()==0 ) {
832 setAppendItemName(getAppendNameNumber(Resource_Fields
[i
]), rbPattern
);
837 // add available formats
838 UBool firstTimeThrough
= TRUE
;
841 UBool override
= TRUE
;
843 // At the start of the loop:
844 // - rb is the open resource bundle for the current locale being processed,
845 // whose actual name is in curLocaleName.
846 // - if U_SUCCESS(err), then calBundle and calTypeBundle are open;
847 // process contents of calTypeBundle, then close calBundle and calTypeBundle.
848 if (U_SUCCESS(err
)) {
849 // process contents of calTypeBundle
850 patBundle
= ures_getByKeyWithFallback(calTypeBundle
, DT_DateTimeAvailableFormatsTag
, NULL
, &err
);
851 if (U_SUCCESS(err
)) {
852 int32_t numberKeys
= ures_getSize(patBundle
);
854 const UChar
*retPattern
;
856 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
857 UResourceBundleAIterator aiter
;
858 ures_a_open(&aiter
, patBundle
, &err
);
860 for(i
=0; i
<numberKeys
; ++i
) {
861 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
862 retPattern
=ures_a_getNextString(&aiter
, &len
, &key
, &err
);
864 retPattern
=ures_getNextString(patBundle
, &len
, &key
, &err
);
866 UnicodeString format
=UnicodeString(retPattern
);
867 UnicodeString retKey
=UnicodeString(key
, -1, US_INV
);
868 if ( firstTimeThrough
|| !isAvailableFormatSet(retKey
) ) {
869 setAvailableFormat(retKey
, err
);
870 // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
871 // but not a previous availableFormats entry:
872 addPatternWithSkeleton(format
, &retKey
, override
, conflictingPattern
, err
);
875 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
876 ures_a_close(&aiter
);
878 ures_close(patBundle
);
880 firstTimeThrough
= FALSE
;
881 // close calBundle and calTypeBundle
882 ures_close(calTypeBundle
);
883 ures_close(calBundle
);
885 if (uprv_strcmp(curLocaleName
,"root")==0 || uprv_strlen(curLocaleName
)==0) {
886 // we just finished handling root, nothing more to check
890 // Find the name of the appropriate parent locale (from %%Parent if present, else
891 // uloc_getParent on the actual locale name)
892 // (It would be nice to have a ures function that did this...)
894 char parentLocale
[ULOC_FULLNAME_CAPACITY
];
896 const UChar
* parentUName
= ures_getStringByKey(rb
, "%%Parent", &locNameLen
, &err
);
897 if (U_SUCCESS(err
) && err
!= U_USING_FALLBACK_WARNING
&& locNameLen
< ULOC_FULLNAME_CAPACITY
) {
898 u_UCharsToChars(parentUName
, parentLocale
, locNameLen
+ 1);
901 uloc_getParent(curLocaleName
, parentLocale
, ULOC_FULLNAME_CAPACITY
, &err
);
902 if (U_FAILURE(err
) || err
== U_STRING_NOT_TERMINATED_WARNING
) {
903 // just fallback to root, since we are not already there
908 // Close current locale bundle
910 // And open its parent, which becomes the new current locale being processed
911 rb
= ures_open(NULL
, parentLocale
, &err
);
912 if ( U_FAILURE(err
) ) {
916 // Get the name of the parent / new current locale
917 curLocaleName
=ures_getLocaleByType(rb
, ULOC_ACTUAL_LOCALE
, &err
);
918 if ( U_FAILURE(err
) ) {
919 curLocaleName
= parentLocale
;
922 if (uprv_strcmp(curLocaleName
,"root")==0 || uprv_strlen(curLocaleName
)==0) {
925 // Open calBundle and calTypeBundle
926 calBundle
= ures_getByKeyWithFallback(rb
, DT_DateTimeCalendarTag
, NULL
, &err
);
927 if (U_SUCCESS(err
)) {
928 calTypeBundle
= ures_getByKeyWithFallback(calBundle
, calendarTypeToUse
, NULL
, &err
);
929 if ( U_FAILURE(err
) ) {
930 ures_close(calBundle
);
933 // Go to the top of the loop to process contents of calTypeBundle
936 if (hackPattern
.length()>0) {
937 hackTimes(hackPattern
, err
);
942 DateTimePatternGenerator::initHashtable(UErrorCode
& err
) {
943 if (fAvailableFormatKeyHash
!=NULL
) {
946 if ((fAvailableFormatKeyHash
= new Hashtable(FALSE
, err
))==NULL
) {
947 err
=U_MEMORY_ALLOCATION_ERROR
;
954 DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field
, const UnicodeString
& value
) {
955 appendItemFormats
[field
] = value
;
956 // NUL-terminate for the C API.
957 appendItemFormats
[field
].getTerminatedBuffer();
961 DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field
) const {
962 return appendItemFormats
[field
];
966 DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field
, const UnicodeString
& value
) {
967 appendItemNames
[field
] = value
;
968 // NUL-terminate for the C API.
969 appendItemNames
[field
].getTerminatedBuffer();
973 DateTimePatternGenerator:: getAppendItemName(UDateTimePatternField field
) const {
974 return appendItemNames
[field
];
978 DateTimePatternGenerator::getAppendName(UDateTimePatternField field
, UnicodeString
& value
) {
979 value
= SINGLE_QUOTE
;
980 value
+= appendItemNames
[field
];
981 value
+= SINGLE_QUOTE
;
985 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UErrorCode
& status
) {
986 return getBestPattern(patternForm
, UDATPG_MATCH_NO_OPTIONS
, status
);
990 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
991 const UnicodeString
*bestPattern
=NULL
;
992 UnicodeString dtFormat
;
993 UnicodeString resultPattern
;
994 int32_t flags
= kDTPGNoFlags
;
996 int32_t dateMask
=(1<<UDATPG_DAYPERIOD_FIELD
) - 1;
997 int32_t timeMask
=(1<<UDATPG_FIELD_COUNT
) - 1 - dateMask
;
999 // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
1000 UnicodeString patternFormCopy
= UnicodeString(patternForm
);
1001 UChar hourFormatSkeletonCharForLowJ
= fDefaultHourFormatChar
;
1002 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1003 case UADATPG_FORCE_12_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= LOW_H
; break;
1004 case UADATPG_FORCE_24_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= CAP_H
; break;
1007 int32_t patPos
, patLen
= patternFormCopy
.length();
1008 UBool inQuoted
= FALSE
;
1009 for (patPos
= 0; patPos
< patLen
; patPos
++) {
1010 UChar patChr
= patternFormCopy
.charAt(patPos
);
1011 if (patChr
== SINGLE_QUOTE
) {
1012 inQuoted
= !inQuoted
;
1013 } else if (!inQuoted
) {
1014 if (patChr
== LOW_J
) {
1015 patternFormCopy
.setCharAt(patPos
, hourFormatSkeletonCharForLowJ
);
1016 } else if (patChr
== CAP_C
) {
1017 AllowedHourFormat preferred
;
1018 if (fAllowedHourFormats
[0] != ALLOWED_HOUR_FORMAT_UNKNOWN
) {
1019 preferred
= (AllowedHourFormat
)fAllowedHourFormats
[0];
1021 status
= U_INVALID_FORMAT_ERROR
;
1022 return UnicodeString();
1025 if (preferred
== ALLOWED_HOUR_FORMAT_H
|| preferred
== ALLOWED_HOUR_FORMAT_HB
|| preferred
== ALLOWED_HOUR_FORMAT_Hb
) {
1026 patternFormCopy
.setCharAt(patPos
, CAP_H
);
1028 patternFormCopy
.setCharAt(patPos
, LOW_H
);
1031 if (preferred
== ALLOWED_HOUR_FORMAT_HB
|| preferred
== ALLOWED_HOUR_FORMAT_hB
) {
1032 flags
|= kDTPGSkeletonUsesCapB
;
1033 } else if (preferred
== ALLOWED_HOUR_FORMAT_Hb
|| preferred
== ALLOWED_HOUR_FORMAT_hb
) {
1034 flags
|= kDTPGSkeletonUsesLowB
;
1036 } else if (patChr
== CAP_J
) {
1037 // Get pattern for skeleton with H, then in adjustFieldTypes
1038 // replace hour pattern characters as necessary.
1039 patternFormCopy
.setCharAt(patPos
, CAP_H
);
1040 flags
|= kDTPGSkeletonUsesCapJ
;
1045 resultPattern
.remove();
1046 dtMatcher
->set(patternFormCopy
, fp
);
1047 const PtnSkeleton
* specifiedSkeleton
=NULL
;
1048 bestPattern
=getBestRaw(*dtMatcher
, -1, distanceInfo
, &specifiedSkeleton
);
1049 if ( distanceInfo
->missingFieldMask
==0 && distanceInfo
->extraFieldMask
==0 ) {
1050 resultPattern
= adjustFieldTypes(*bestPattern
, specifiedSkeleton
, flags
, options
);
1052 return resultPattern
;
1054 int32_t neededFields
= dtMatcher
->getFieldMask();
1055 UnicodeString datePattern
=getBestAppending(neededFields
& dateMask
, flags
, options
);
1056 UnicodeString timePattern
=getBestAppending(neededFields
& timeMask
, flags
, options
);
1057 if (datePattern
.length()==0) {
1058 if (timePattern
.length()==0) {
1059 resultPattern
.remove();
1065 if (timePattern
.length()==0) {
1068 resultPattern
.remove();
1069 status
= U_ZERO_ERROR
;
1070 dtFormat
=getDateTimeFormat();
1071 SimpleFormatter(dtFormat
, 2, 2, status
).format(timePattern
, datePattern
, resultPattern
, status
);
1072 return resultPattern
;
1076 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1077 const UnicodeString
& skeleton
,
1078 UErrorCode
& status
) {
1079 return replaceFieldTypes(pattern
, skeleton
, UDATPG_MATCH_NO_OPTIONS
, status
);
1083 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1084 const UnicodeString
& skeleton
,
1085 UDateTimePatternMatchOptions options
,
1086 UErrorCode
& /*status*/) {
1087 dtMatcher
->set(skeleton
, fp
);
1088 UnicodeString result
= adjustFieldTypes(pattern
, NULL
, kDTPGNoFlags
, options
);
1093 DateTimePatternGenerator::setDecimal(const UnicodeString
& newDecimal
) {
1094 this->decimal
= newDecimal
;
1095 // NUL-terminate for the C API.
1096 this->decimal
.getTerminatedBuffer();
1099 const UnicodeString
&
1100 DateTimePatternGenerator::getDecimal() const {
1105 DateTimePatternGenerator::addCanonicalItems() {
1106 UnicodeString conflictingPattern
;
1107 UErrorCode status
= U_ZERO_ERROR
;
1109 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; i
++) {
1110 addPattern(UnicodeString(Canonical_Items
[i
]), FALSE
, conflictingPattern
, status
);
1115 DateTimePatternGenerator::setDateTimeFormat(const UnicodeString
& dtFormat
) {
1116 dateTimeFormat
= dtFormat
;
1117 // NUL-terminate for the C API.
1118 dateTimeFormat
.getTerminatedBuffer();
1121 const UnicodeString
&
1122 DateTimePatternGenerator::getDateTimeFormat() const {
1123 return dateTimeFormat
;
1127 DateTimePatternGenerator::setDateTimeFromCalendar(const Locale
& locale
, UErrorCode
& status
) {
1128 const UChar
*resStr
;
1129 int32_t resStrLen
= 0;
1131 Calendar
* fCalendar
= Calendar::createInstance(locale
, status
);
1132 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
1133 UResourceBundle
*dateTimePatterns
= calData
.getByKey(DT_DateTimePatternsTag
, status
);
1134 if (U_FAILURE(status
)) return;
1136 if (ures_getSize(dateTimePatterns
) <= DateFormat::kDateTime
)
1138 status
= U_INVALID_FORMAT_ERROR
;
1141 resStr
= ures_getStringByIndex(dateTimePatterns
, (int32_t)DateFormat::kDateTime
, &resStrLen
, &status
);
1142 setDateTimeFormat(UnicodeString(TRUE
, resStr
, resStrLen
));
1148 DateTimePatternGenerator::setDecimalSymbols(const Locale
& locale
, UErrorCode
& status
) {
1149 DecimalFormatSymbols dfs
= DecimalFormatSymbols(locale
, status
);
1150 if(U_SUCCESS(status
)) {
1151 decimal
= dfs
.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol
);
1152 // NUL-terminate for the C API.
1153 decimal
.getTerminatedBuffer();
1157 UDateTimePatternConflict
1158 DateTimePatternGenerator::addPattern(
1159 const UnicodeString
& pattern
,
1161 UnicodeString
&conflictingPattern
,
1164 return addPatternWithSkeleton(pattern
, NULL
, override
, conflictingPattern
, status
);
1167 // For DateTimePatternGenerator::addPatternWithSkeleton -
1168 // If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
1169 // 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern.
1170 // 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
1171 // (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
1172 // parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
1173 // specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
1174 // derived (i.e. entries derived from the standard date/time patters for the specified locale).
1175 // 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a
1176 // specified skeleton (which sets a new field in the PtnElem in the PatternMap).
1177 UDateTimePatternConflict
1178 DateTimePatternGenerator::addPatternWithSkeleton(
1179 const UnicodeString
& pattern
,
1180 const UnicodeString
* skeletonToUse
,
1182 UnicodeString
& conflictingPattern
,
1186 UnicodeString basePattern
;
1187 PtnSkeleton skeleton
;
1188 UDateTimePatternConflict conflictingStatus
= UDATPG_NO_CONFLICT
;
1190 DateTimeMatcher matcher
;
1191 if ( skeletonToUse
== NULL
) {
1192 matcher
.set(pattern
, fp
, skeleton
);
1193 matcher
.getBasePattern(basePattern
);
1195 matcher
.set(*skeletonToUse
, fp
, skeleton
); // no longer trims skeleton fields to max len 3, per #7930
1196 matcher
.getBasePattern(basePattern
); // or perhaps instead: basePattern = *skeletonToUse;
1198 // We only care about base conflicts - and replacing the pattern associated with a base - if:
1199 // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
1200 // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
1201 // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
1202 // if we are getting here from a subsequent call to addPattern).
1203 // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
1204 // availableFormats items from root, which should not override any previous entry with the same base.
1205 UBool entryHadSpecifiedSkeleton
;
1206 const UnicodeString
*duplicatePattern
= patternMap
->getPatternFromBasePattern(basePattern
, entryHadSpecifiedSkeleton
);
1207 if (duplicatePattern
!= NULL
&& (!entryHadSpecifiedSkeleton
|| (skeletonToUse
!= NULL
&& !override
))) {
1208 conflictingStatus
= UDATPG_BASE_CONFLICT
;
1209 conflictingPattern
= *duplicatePattern
;
1211 return conflictingStatus
;
1214 // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
1215 // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
1216 // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
1217 // the previously-specified conflicting item.
1218 const PtnSkeleton
* entrySpecifiedSkeleton
= NULL
;
1219 duplicatePattern
= patternMap
->getPatternFromSkeleton(skeleton
, &entrySpecifiedSkeleton
);
1220 if (duplicatePattern
!= NULL
) {
1221 conflictingStatus
= UDATPG_CONFLICT
;
1222 conflictingPattern
= *duplicatePattern
;
1223 if (!override
|| (skeletonToUse
!= NULL
&& entrySpecifiedSkeleton
!= NULL
)) {
1224 return conflictingStatus
;
1227 patternMap
->add(basePattern
, skeleton
, pattern
, skeletonToUse
!= NULL
, status
);
1228 if(U_FAILURE(status
)) {
1229 return conflictingStatus
;
1232 return UDATPG_NO_CONFLICT
;
1236 UDateTimePatternField
1237 DateTimePatternGenerator::getAppendFormatNumber(const char* field
) const {
1238 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1239 if (uprv_strcmp(CLDR_FIELD_APPEND
[i
], field
)==0) {
1240 return (UDateTimePatternField
)i
;
1243 return UDATPG_FIELD_COUNT
;
1246 UDateTimePatternField
1247 DateTimePatternGenerator::getAppendNameNumber(const char* field
) const {
1248 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1249 if (uprv_strcmp(CLDR_FIELD_NAME
[i
],field
)==0) {
1250 return (UDateTimePatternField
)i
;
1253 return UDATPG_FIELD_COUNT
;
1256 const UnicodeString
*
1257 DateTimePatternGenerator::getBestRaw(DateTimeMatcher
& source
,
1258 int32_t includeMask
,
1259 DistanceInfo
* missingFields
,
1260 const PtnSkeleton
** specifiedSkeletonPtr
) {
1261 int32_t bestDistance
= 0x7fffffff;
1262 DistanceInfo tempInfo
;
1263 const UnicodeString
*bestPattern
=NULL
;
1264 const PtnSkeleton
* specifiedSkeleton
=NULL
;
1266 PatternMapIterator it
;
1267 for (it
.set(*patternMap
); it
.hasNext(); ) {
1268 DateTimeMatcher trial
= it
.next();
1269 if (trial
.equals(skipMatcher
)) {
1272 int32_t distance
=source
.getDistance(trial
, includeMask
, tempInfo
);
1273 if (distance
<bestDistance
) {
1274 bestDistance
=distance
;
1275 bestPattern
=patternMap
->getPatternFromSkeleton(*trial
.getSkeletonPtr(), &specifiedSkeleton
);
1276 missingFields
->setTo(tempInfo
);
1283 // If the best raw match had a specified skeleton and that skeleton was requested by the caller,
1284 // then return it too. This generally happens when the caller needs to pass that skeleton
1285 // through to adjustFieldTypes so the latter can do a better job.
1286 if (bestPattern
&& specifiedSkeletonPtr
) {
1287 *specifiedSkeletonPtr
= specifiedSkeleton
;
1293 DateTimePatternGenerator::adjustFieldTypes(const UnicodeString
& pattern
,
1294 const PtnSkeleton
* specifiedSkeleton
,
1296 UDateTimePatternMatchOptions options
) {
1297 UnicodeString newPattern
;
1299 for (int32_t i
=0; i
< fp
->itemNumber
; i
++) {
1300 UnicodeString field
= fp
->items
[i
];
1301 if ( fp
->isQuoteLiteral(field
) ) {
1303 UnicodeString quoteLiteral
;
1304 fp
->getQuoteLiteral(quoteLiteral
, &i
);
1305 newPattern
+= quoteLiteral
;
1308 if (fp
->isPatternSeparator(field
)) {
1312 int32_t canonicalIndex
= fp
->getCanonicalIndex(field
);
1313 if (canonicalIndex
< 0) {
1315 continue; // don't adjust
1317 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
1318 int32_t typeValue
= row
->field
;
1320 // Handle special day periods.
1321 if (typeValue
== UDATPG_DAYPERIOD_FIELD
&& flags
!= 0) {
1322 UChar c
= NONE
; // '0'
1323 if (flags
& kDTPGSkeletonUsesCapB
) { c
= CAP_B
; }
1324 if (flags
& kDTPGSkeletonUsesLowB
) { c
= LOW_B
; }
1327 for (int32_t i
= 0; i
< field
.length(); ++i
)
1328 field
.setCharAt(i
, c
);
1332 if ((flags
& kDTPGFixFractionalSeconds
) != 0 && typeValue
== UDATPG_SECOND_FIELD
) {
1333 UnicodeString newField
=dtMatcher
->skeleton
.original
[UDATPG_FRACTIONAL_SECOND_FIELD
];
1334 field
= field
+ decimal
+ newField
;
1335 } else if (dtMatcher
->skeleton
.type
[typeValue
]!=0) {
1337 // - "reqField" is the field from the originally requested skeleton, with length
1339 // - "field" is the field from the found pattern.
1341 // The adjusted field should consist of characters from the originally requested
1342 // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
1343 // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
1344 // of characters from the found pattern.
1346 // The length of the adjusted field (adjFieldLen) should match that in the originally
1347 // requested skeleton, except that in the following cases the length of the adjusted field
1348 // should match that in the found pattern (i.e. the length of this pattern field should
1349 // not be adjusted):
1350 // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is
1351 // not set (ticket #7180). Note, we may want to implement a similar change for other
1352 // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for
1353 // field length, but options bits can be used to override this.
1354 // 2. There is a specified skeleton for the found pattern and one of the following is true:
1355 // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
1356 // b) The pattern field is numeric and the skeleton field is not, or vice versa.
1358 UnicodeString reqField
= dtMatcher
->skeleton
.original
[typeValue
];
1359 int32_t reqFieldLen
= reqField
.length();
1360 if (reqField
.charAt(0) == CAP_E
&& reqFieldLen
< 3)
1361 reqFieldLen
= 3; // 1-3 for E are equivalent to 3 for c,e
1362 int32_t adjFieldLen
= reqFieldLen
;
1363 if ( (typeValue
==UDATPG_HOUR_FIELD
&& (options
& UDATPG_MATCH_HOUR_FIELD_LENGTH
)==0) ||
1364 (typeValue
==UDATPG_MINUTE_FIELD
&& (options
& UDATPG_MATCH_MINUTE_FIELD_LENGTH
)==0) ||
1365 (typeValue
==UDATPG_SECOND_FIELD
&& (options
& UDATPG_MATCH_SECOND_FIELD_LENGTH
)==0) ) {
1366 adjFieldLen
= field
.length();
1367 } else if (specifiedSkeleton
) {
1368 UnicodeString skelField
= specifiedSkeleton
->original
[typeValue
];
1369 int32_t skelFieldLen
= skelField
.length();
1370 UBool patFieldIsNumeric
= (row
->type
> 0);
1371 UBool skelFieldIsNumeric
= (specifiedSkeleton
->type
[typeValue
] > 0);
1372 if (skelFieldLen
== reqFieldLen
|| (patFieldIsNumeric
&& !skelFieldIsNumeric
) || (skelFieldIsNumeric
&& !patFieldIsNumeric
)) {
1373 // don't adjust the field length in the found pattern
1374 adjFieldLen
= field
.length();
1377 UChar c
= (typeValue
!= UDATPG_HOUR_FIELD
&& typeValue
!= UDATPG_MONTH_FIELD
&&
1378 typeValue
!= UDATPG_WEEKDAY_FIELD
&& (typeValue
!= UDATPG_YEAR_FIELD
|| reqField
.charAt(0)==CAP_Y
))?
1379 reqField
.charAt(0): field
.charAt(0);
1380 if (typeValue
== UDATPG_HOUR_FIELD
&& (flags
& kDTPGSkeletonUsesCapJ
) != 0) {
1381 c
= fDefaultHourFormatChar
;
1382 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1383 case UADATPG_FORCE_12_HOUR_CYCLE
:
1384 if (c
== CAP_H
|| c
== LOW_K
) {
1385 // Have 24-hour cycle, change to 12-hour cycle.
1386 // Should have better way to pick 'h' or 'K'.
1390 case UADATPG_FORCE_24_HOUR_CYCLE
:
1391 if (c
== LOW_H
|| c
== CAP_K
) {
1392 // Have 12-hour cycle, change to 24-hour cycle.
1393 // Should have better way to pick 'H' or 'k'.
1402 for (int32_t i
=adjFieldLen
; i
>0; --i
) {
1413 DateTimePatternGenerator::getBestAppending(int32_t missingFields
, int32_t flags
, UDateTimePatternMatchOptions options
) {
1414 UnicodeString resultPattern
, tempPattern
;
1415 UErrorCode err
=U_ZERO_ERROR
;
1416 int32_t lastMissingFieldMask
=0;
1417 if (missingFields
!=0) {
1418 resultPattern
=UnicodeString();
1419 const PtnSkeleton
* specifiedSkeleton
=NULL
;
1420 tempPattern
= *getBestRaw(*dtMatcher
, missingFields
, distanceInfo
, &specifiedSkeleton
);
1421 resultPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1422 if ( distanceInfo
->missingFieldMask
==0 ) {
1423 return resultPattern
;
1425 while (distanceInfo
->missingFieldMask
!=0) { // precondition: EVERY single field must work!
1426 if ( lastMissingFieldMask
== distanceInfo
->missingFieldMask
) {
1427 break; // cannot find the proper missing field
1429 if (((distanceInfo
->missingFieldMask
& UDATPG_SECOND_AND_FRACTIONAL_MASK
)==UDATPG_FRACTIONAL_MASK
) &&
1430 ((missingFields
& UDATPG_SECOND_AND_FRACTIONAL_MASK
) == UDATPG_SECOND_AND_FRACTIONAL_MASK
)) {
1431 resultPattern
= adjustFieldTypes(resultPattern
, specifiedSkeleton
, flags
| kDTPGFixFractionalSeconds
, options
);
1432 distanceInfo
->missingFieldMask
&= ~UDATPG_FRACTIONAL_MASK
;
1435 int32_t startingMask
= distanceInfo
->missingFieldMask
;
1436 tempPattern
= *getBestRaw(*dtMatcher
, distanceInfo
->missingFieldMask
, distanceInfo
, &specifiedSkeleton
);
1437 tempPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1438 int32_t foundMask
=startingMask
& ~distanceInfo
->missingFieldMask
;
1439 int32_t topField
=getTopBitNumber(foundMask
);
1440 UnicodeString appendName
;
1441 getAppendName((UDateTimePatternField
)topField
, appendName
);
1442 const UnicodeString
*values
[3] = {
1447 SimpleFormatter(appendItemFormats
[topField
], 2, 3, err
).
1448 formatAndReplace(values
, 3, resultPattern
, NULL
, 0, err
);
1449 lastMissingFieldMask
= distanceInfo
->missingFieldMask
;
1452 return resultPattern
;
1456 DateTimePatternGenerator::getTopBitNumber(int32_t foundMask
) {
1457 if ( foundMask
==0 ) {
1461 while (foundMask
!=0) {
1465 if (i
-1 >UDATPG_ZONE_FIELD
) {
1466 return UDATPG_ZONE_FIELD
;
1473 DateTimePatternGenerator::setAvailableFormat(const UnicodeString
&key
, UErrorCode
& err
)
1475 fAvailableFormatKeyHash
->puti(key
, 1, err
);
1479 DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString
&key
) const {
1480 return (UBool
)(fAvailableFormatKeyHash
->geti(key
) == 1);
1484 DateTimePatternGenerator::copyHashtable(Hashtable
*other
, UErrorCode
&status
) {
1486 if (other
== NULL
) {
1489 if (fAvailableFormatKeyHash
!= NULL
) {
1490 delete fAvailableFormatKeyHash
;
1491 fAvailableFormatKeyHash
= NULL
;
1493 initHashtable(status
);
1494 if(U_FAILURE(status
)){
1497 int32_t pos
= UHASH_FIRST
;
1498 const UHashElement
* elem
= NULL
;
1499 // walk through the hash table and create a deep clone
1500 while((elem
= other
->nextElement(pos
))!= NULL
){
1501 const UHashTok otherKeyTok
= elem
->key
;
1502 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
1503 fAvailableFormatKeyHash
->puti(*otherKey
, 1, status
);
1504 if(U_FAILURE(status
)){
1511 DateTimePatternGenerator::getSkeletons(UErrorCode
& status
) const {
1512 StringEnumeration
* skeletonEnumerator
= new DTSkeletonEnumeration(*patternMap
, DT_SKELETON
, status
);
1513 return skeletonEnumerator
;
1516 const UnicodeString
&
1517 DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString
& skeleton
) const {
1520 if (skeleton
.length() ==0) {
1523 curElem
= patternMap
->getHeader(skeleton
.charAt(0));
1524 while ( curElem
!= NULL
) {
1525 if ( curElem
->skeleton
->getSkeleton()==skeleton
) {
1526 return curElem
->pattern
;
1528 curElem
=curElem
->next
;
1534 DateTimePatternGenerator::getBaseSkeletons(UErrorCode
& status
) const {
1535 StringEnumeration
* baseSkeletonEnumerator
= new DTSkeletonEnumeration(*patternMap
, DT_BASESKELETON
, status
);
1536 return baseSkeletonEnumerator
;
1540 DateTimePatternGenerator::getRedundants(UErrorCode
& status
) {
1541 StringEnumeration
* output
= new DTRedundantEnumeration();
1542 const UnicodeString
*pattern
;
1543 PatternMapIterator it
;
1544 for (it
.set(*patternMap
); it
.hasNext(); ) {
1545 DateTimeMatcher current
= it
.next();
1546 pattern
= patternMap
->getPatternFromSkeleton(*(it
.getSkeleton()));
1547 if ( isCanonicalItem(*pattern
) ) {
1550 if ( skipMatcher
== NULL
) {
1551 skipMatcher
= new DateTimeMatcher(current
);
1554 *skipMatcher
= current
;
1556 UnicodeString trial
= getBestPattern(current
.getPattern(), status
);
1557 if (trial
== *pattern
) {
1558 ((DTRedundantEnumeration
*)output
)->add(*pattern
, status
);
1560 if (current
.equals(skipMatcher
)) {
1568 DateTimePatternGenerator::isCanonicalItem(const UnicodeString
& item
) const {
1569 if ( item
.length() != 1 ) {
1572 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1573 if (item
.charAt(0)==Canonical_Items
[i
]) {
1581 DateTimePatternGenerator
*
1582 DateTimePatternGenerator::clone() const {
1583 return new DateTimePatternGenerator(*this);
1586 PatternMap::PatternMap() {
1587 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1590 isDupAllowed
= TRUE
;
1594 PatternMap::copyFrom(const PatternMap
& other
, UErrorCode
& status
) {
1595 this->isDupAllowed
= other
.isDupAllowed
;
1596 for (int32_t bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
1597 PtnElem
*curElem
, *otherElem
, *prevElem
=NULL
;
1598 otherElem
= other
.boot
[bootIndex
];
1599 while (otherElem
!=NULL
) {
1600 if ((curElem
= new PtnElem(otherElem
->basePattern
, otherElem
->pattern
))==NULL
) {
1602 status
= U_MEMORY_ALLOCATION_ERROR
;
1605 if ( this->boot
[bootIndex
]== NULL
) {
1606 this->boot
[bootIndex
] = curElem
;
1608 if ((curElem
->skeleton
=new PtnSkeleton(*(otherElem
->skeleton
))) == NULL
) {
1610 status
= U_MEMORY_ALLOCATION_ERROR
;
1613 curElem
->skeletonWasSpecified
= otherElem
->skeletonWasSpecified
;
1614 if (prevElem
!=NULL
) {
1615 prevElem
->next
=curElem
;
1619 otherElem
= otherElem
->next
;
1626 PatternMap::getHeader(UChar baseChar
) {
1629 if ( (baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
) ) {
1630 curElem
= boot
[baseChar
-CAP_A
];
1633 if ( (baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
) ) {
1634 curElem
= boot
[26+baseChar
-LOW_A
];
1643 PatternMap::~PatternMap() {
1644 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1645 if (boot
[i
]!=NULL
) {
1650 } // PatternMap destructor
1653 PatternMap::add(const UnicodeString
& basePattern
,
1654 const PtnSkeleton
& skeleton
,
1655 const UnicodeString
& value
,// mapped pattern value
1656 UBool skeletonWasSpecified
,
1657 UErrorCode
&status
) {
1658 UChar baseChar
= basePattern
.charAt(0);
1659 PtnElem
*curElem
, *baseElem
;
1660 status
= U_ZERO_ERROR
;
1662 // the baseChar must be A-Z or a-z
1663 if ((baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
)) {
1664 baseElem
= boot
[baseChar
-CAP_A
];
1667 if ((baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
)) {
1668 baseElem
= boot
[26+baseChar
-LOW_A
];
1671 status
= U_ILLEGAL_CHARACTER
;
1676 if (baseElem
== NULL
) {
1677 if ((curElem
= new PtnElem(basePattern
, value
)) == NULL
) {
1679 status
= U_MEMORY_ALLOCATION_ERROR
;
1682 if (baseChar
>= LOW_A
) {
1683 boot
[26 + (baseChar
-LOW_A
)] = curElem
;
1686 boot
[baseChar
-CAP_A
] = curElem
;
1688 curElem
->skeleton
= new PtnSkeleton(skeleton
);
1689 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1691 if ( baseElem
!= NULL
) {
1692 curElem
= getDuplicateElem(basePattern
, skeleton
, baseElem
);
1694 if (curElem
== NULL
) {
1695 // add new element to the list.
1697 while( curElem
-> next
!= NULL
)
1699 curElem
= curElem
->next
;
1701 if ((curElem
->next
= new PtnElem(basePattern
, value
)) == NULL
) {
1703 status
= U_MEMORY_ALLOCATION_ERROR
;
1706 curElem
=curElem
->next
;
1707 curElem
->skeleton
= new PtnSkeleton(skeleton
);
1708 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1711 // Pattern exists in the list already.
1712 if ( !isDupAllowed
) {
1715 // Overwrite the value.
1716 curElem
->pattern
= value
;
1717 // It was a bug that we were not doing the following previously,
1718 // though that bug hid other problems by making things partly work.
1719 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1722 } // PatternMap::add
1724 // Find the pattern from the given basePattern string.
1725 const UnicodeString
*
1726 PatternMap::getPatternFromBasePattern(UnicodeString
& basePattern
, UBool
& skeletonWasSpecified
) { // key to search for
1729 if ((curElem
=getHeader(basePattern
.charAt(0)))==NULL
) {
1730 return NULL
; // no match
1734 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
1735 skeletonWasSpecified
= curElem
->skeletonWasSpecified
;
1736 return &(curElem
->pattern
);
1738 curElem
=curElem
->next
;
1739 }while (curElem
!= NULL
);
1742 } // PatternMap::getFromBasePattern
1745 // Find the pattern from the given skeleton.
1746 // At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL),
1747 // the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw)
1748 // and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the
1749 // optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL),
1750 // for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily.
1751 const UnicodeString
*
1752 PatternMap::getPatternFromSkeleton(PtnSkeleton
& skeleton
, const PtnSkeleton
** specifiedSkeletonPtr
) { // key to search for
1755 if (specifiedSkeletonPtr
) {
1756 *specifiedSkeletonPtr
= NULL
;
1760 UChar baseChar
='\0';
1761 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1762 if (skeleton
.baseOriginal
[i
].length() !=0 ) {
1763 baseChar
= skeleton
.baseOriginal
[i
].charAt(0);
1768 if ((curElem
=getHeader(baseChar
))==NULL
) {
1769 return NULL
; // no match
1774 if (specifiedSkeletonPtr
!= NULL
) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
1775 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1776 if (curElem
->skeleton
->original
[i
].compare(skeleton
.original
[i
]) != 0 )
1781 } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
1782 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1783 if (curElem
->skeleton
->baseOriginal
[i
].compare(skeleton
.baseOriginal
[i
]) != 0 )
1789 if (i
== UDATPG_FIELD_COUNT
) {
1790 if (specifiedSkeletonPtr
&& curElem
->skeletonWasSpecified
) {
1791 *specifiedSkeletonPtr
= curElem
->skeleton
;
1793 return &(curElem
->pattern
);
1795 curElem
=curElem
->next
;
1796 }while (curElem
!= NULL
);
1802 PatternMap::equals(const PatternMap
& other
) {
1803 if ( this==&other
) {
1806 for (int32_t bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
1807 if ( boot
[bootIndex
]==other
.boot
[bootIndex
] ) {
1810 if ( (boot
[bootIndex
]==NULL
)||(other
.boot
[bootIndex
]==NULL
) ) {
1813 PtnElem
*otherElem
= other
.boot
[bootIndex
];
1814 PtnElem
*myElem
= boot
[bootIndex
];
1815 while ((otherElem
!=NULL
) || (myElem
!=NULL
)) {
1816 if ( myElem
== otherElem
) {
1819 if ((otherElem
==NULL
) || (myElem
==NULL
)) {
1822 if ( (myElem
->basePattern
!= otherElem
->basePattern
) ||
1823 (myElem
->pattern
!= otherElem
->pattern
) ) {
1826 if ((myElem
->skeleton
!=otherElem
->skeleton
)&&
1827 !myElem
->skeleton
->equals(*(otherElem
->skeleton
))) {
1830 myElem
= myElem
->next
;
1831 otherElem
=otherElem
->next
;
1837 // find any key existing in the mapping table already.
1838 // return TRUE if there is an existing key, otherwise return FALSE.
1840 PatternMap::getDuplicateElem(
1841 const UnicodeString
&basePattern
,
1842 const PtnSkeleton
&skeleton
,
1843 PtnElem
*baseElem
) {
1846 if ( baseElem
== (PtnElem
*)NULL
) {
1847 return (PtnElem
*)NULL
;
1853 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
1855 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1856 if (curElem
->skeleton
->type
[i
] != skeleton
.type
[i
] ) {
1865 curElem
= curElem
->next
;
1866 } while( curElem
!= (PtnElem
*)NULL
);
1869 return (PtnElem
*)NULL
;
1871 } // PatternMap::getDuplicateElem
1873 DateTimeMatcher::DateTimeMatcher(void) {
1876 DateTimeMatcher::~DateTimeMatcher() {}
1878 DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher
& other
) {
1879 copyFrom(other
.skeleton
);
1884 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
) {
1885 PtnSkeleton localSkeleton
;
1886 return set(pattern
, fp
, localSkeleton
);
1890 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
, PtnSkeleton
& skeletonResult
) {
1892 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1893 skeletonResult
.type
[i
]=NONE
;
1896 for (i
=0; i
< fp
->itemNumber
; i
++) {
1897 UnicodeString field
= fp
->items
[i
];
1898 if ( field
.charAt(0) == LOW_A
) {
1899 continue; // skip 'a'
1902 if ( fp
->isQuoteLiteral(field
) ) {
1903 UnicodeString quoteLiteral
;
1904 fp
->getQuoteLiteral(quoteLiteral
, &i
);
1907 int32_t canonicalIndex
= fp
->getCanonicalIndex(field
);
1908 if (canonicalIndex
< 0 ) {
1911 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
1912 int32_t typeValue
= row
->field
;
1913 skeletonResult
.original
[typeValue
]=field
;
1914 UChar repeatChar
= row
->patternChar
;
1915 int32_t repeatCount
= row
->minLen
; // #7930 removes cap at 3
1916 while (repeatCount
-- > 0) {
1917 skeletonResult
.baseOriginal
[typeValue
] += repeatChar
;
1919 int16_t subTypeValue
= row
->type
;
1920 if ( row
->type
> 0) {
1921 subTypeValue
+= field
.length();
1923 skeletonResult
.type
[typeValue
] = subTypeValue
;
1925 copyFrom(skeletonResult
);
1929 DateTimeMatcher::getBasePattern(UnicodeString
&result
) {
1930 result
.remove(); // Reset the result first.
1931 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1932 if (skeleton
.baseOriginal
[i
].length()!=0) {
1933 result
+= skeleton
.baseOriginal
[i
];
1939 DateTimeMatcher::getPattern() {
1940 UnicodeString result
;
1942 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1943 if (skeleton
.original
[i
].length()!=0) {
1944 result
+= skeleton
.original
[i
];
1951 DateTimeMatcher::getDistance(const DateTimeMatcher
& other
, int32_t includeMask
, DistanceInfo
& distanceInfo
) {
1953 distanceInfo
.clear();
1954 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1955 int32_t myType
= (includeMask
&(1<<i
))==0 ? 0 : skeleton
.type
[i
];
1956 int32_t otherType
= other
.skeleton
.type
[i
];
1957 if (myType
==otherType
) {
1960 if (myType
==0) {// and other is not
1961 result
+= EXTRA_FIELD
;
1962 distanceInfo
.addExtra(i
);
1966 result
+= MISSING_FIELD
;
1967 distanceInfo
.addMissing(i
);
1970 result
+= abs(myType
- otherType
);
1979 DateTimeMatcher::copyFrom(const PtnSkeleton
& newSkeleton
) {
1980 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1981 this->skeleton
.type
[i
]=newSkeleton
.type
[i
];
1982 this->skeleton
.original
[i
]=newSkeleton
.original
[i
];
1983 this->skeleton
.baseOriginal
[i
]=newSkeleton
.baseOriginal
[i
];
1988 DateTimeMatcher::copyFrom() {
1990 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1991 this->skeleton
.type
[i
]=0;
1992 this->skeleton
.original
[i
].remove();
1993 this->skeleton
.baseOriginal
[i
].remove();
1998 DateTimeMatcher::equals(const DateTimeMatcher
* other
) const {
2002 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2003 if (this->skeleton
.original
[i
]!=other
->skeleton
.original
[i
] ) {
2011 DateTimeMatcher::getFieldMask() {
2014 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2015 if (skeleton
.type
[i
]!=0) {
2023 DateTimeMatcher::getSkeletonPtr() {
2027 FormatParser::FormatParser () {
2033 FormatParser::~FormatParser () {
2037 // Find the next token with the starting position and length
2038 // Note: the startPos may
2039 FormatParser::TokenStatus
2040 FormatParser::setTokens(const UnicodeString
& pattern
, int32_t startPos
, int32_t *len
) {
2041 int32_t curLoc
= startPos
;
2042 if ( curLoc
>= pattern
.length()) {
2045 // check the current char is between A-Z or a-z
2047 UChar c
=pattern
.charAt(curLoc
);
2048 if ( (c
>=CAP_A
&& c
<=CAP_Z
) || (c
>=LOW_A
&& c
<=LOW_Z
) ) {
2057 if ( pattern
.charAt(curLoc
)!= pattern
.charAt(startPos
) ) {
2058 break; // not the same token
2060 } while(curLoc
<= pattern
.length());
2061 *len
= curLoc
-startPos
;
2066 FormatParser::set(const UnicodeString
& pattern
) {
2068 TokenStatus result
=START
;
2073 result
= setTokens( pattern
, startPos
, &len
);
2074 if ( result
== ADD_TOKEN
)
2076 items
[itemNumber
++] = UnicodeString(pattern
, startPos
, len
);
2082 } while (result
==ADD_TOKEN
&& itemNumber
< MAX_DT_TOKEN
);
2086 FormatParser::getCanonicalIndex(const UnicodeString
& s
, UBool strict
) {
2087 int32_t len
= s
.length();
2091 UChar ch
= s
.charAt(0);
2093 // Verify that all are the same character.
2094 for (int32_t l
= 1; l
< len
; l
++) {
2095 if (ch
!= s
.charAt(l
)) {
2100 int32_t bestRow
= -1;
2101 while (dtTypes
[i
].patternChar
!= '\0') {
2102 if ( dtTypes
[i
].patternChar
!= ch
) {
2107 if (dtTypes
[i
].patternChar
!= dtTypes
[i
+1].patternChar
) {
2110 if (dtTypes
[i
+1].minLen
<= len
) {
2116 return strict
? -1 : bestRow
;
2120 FormatParser::isQuoteLiteral(const UnicodeString
& s
) {
2121 return (UBool
)(s
.charAt(0)==SINGLE_QUOTE
);
2124 // This function aussumes the current itemIndex points to the quote literal.
2125 // Please call isQuoteLiteral prior to this function.
2127 FormatParser::getQuoteLiteral(UnicodeString
& quote
, int32_t *itemIndex
) {
2128 int32_t i
=*itemIndex
;
2131 if (items
[i
].charAt(0)==SINGLE_QUOTE
) {
2135 while ( i
< itemNumber
) {
2136 if ( items
[i
].charAt(0)==SINGLE_QUOTE
) {
2137 if ( (i
+1<itemNumber
) && (items
[i
+1].charAt(0)==SINGLE_QUOTE
)) {
2138 // two single quotes e.g. 'o''clock'
2139 quote
+= items
[i
++];
2140 quote
+= items
[i
++];
2157 FormatParser::isPatternSeparator(UnicodeString
& field
) {
2158 for (int32_t i
=0; i
<field
.length(); ++i
) {
2159 UChar c
= field
.charAt(i
);
2160 if ( (c
==SINGLE_QUOTE
) || (c
==BACKSLASH
) || (c
==SPACE
) || (c
==COLON
) ||
2161 (c
==QUOTATION_MARK
) || (c
==COMMA
) || (c
==HYPHEN
) ||(items
[i
].charAt(0)==DOT
) ) {
2171 DistanceInfo::~DistanceInfo() {}
2174 DistanceInfo::setTo(DistanceInfo
&other
) {
2175 missingFieldMask
= other
.missingFieldMask
;
2176 extraFieldMask
= other
.extraFieldMask
;
2179 PatternMapIterator::PatternMapIterator() {
2183 matcher
= new DateTimeMatcher();
2187 PatternMapIterator::~PatternMapIterator() {
2192 PatternMapIterator::set(PatternMap
& newPatternMap
) {
2193 this->patternMap
=&newPatternMap
;
2197 PatternMapIterator::getSkeleton() {
2198 if ( nodePtr
== NULL
) {
2202 return nodePtr
->skeleton
;
2207 PatternMapIterator::hasNext() {
2208 int32_t headIndex
=bootIndex
;
2209 PtnElem
*curPtr
=nodePtr
;
2211 if (patternMap
==NULL
) {
2214 while ( headIndex
< MAX_PATTERN_ENTRIES
) {
2215 if ( curPtr
!= NULL
) {
2216 if ( curPtr
->next
!= NULL
) {
2226 if ( patternMap
->boot
[headIndex
] != NULL
) {
2240 PatternMapIterator::next() {
2241 while ( bootIndex
< MAX_PATTERN_ENTRIES
) {
2242 if ( nodePtr
!= NULL
) {
2243 if ( nodePtr
->next
!= NULL
) {
2244 nodePtr
= nodePtr
->next
;
2254 if ( patternMap
->boot
[bootIndex
] != NULL
) {
2255 nodePtr
= patternMap
->boot
[bootIndex
];
2264 if (nodePtr
!=NULL
) {
2265 matcher
->copyFrom(*nodePtr
->skeleton
);
2268 matcher
->copyFrom();
2273 PtnSkeleton::PtnSkeleton() {
2277 PtnSkeleton::PtnSkeleton(const PtnSkeleton
& other
) {
2278 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2279 this->type
[i
]=other
.type
[i
];
2280 this->original
[i
]=other
.original
[i
];
2281 this->baseOriginal
[i
]=other
.baseOriginal
[i
];
2286 PtnSkeleton::equals(const PtnSkeleton
& other
) {
2287 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2288 if ( (type
[i
]!= other
.type
[i
]) ||
2289 (original
[i
]!=other
.original
[i
]) ||
2290 (baseOriginal
[i
]!=other
.baseOriginal
[i
]) ) {
2298 PtnSkeleton::getSkeleton() {
2299 UnicodeString result
;
2301 for(int32_t i
=0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2302 if (original
[i
].length()!=0) {
2303 result
+= original
[i
];
2310 PtnSkeleton::getBaseSkeleton() {
2311 UnicodeString result
;
2313 for(int32_t i
=0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2314 if (baseOriginal
[i
].length()!=0) {
2315 result
+= baseOriginal
[i
];
2321 PtnSkeleton::~PtnSkeleton() {
2324 PtnElem::PtnElem(const UnicodeString
&basePat
, const UnicodeString
&pat
) :
2325 basePattern(basePat
),
2332 PtnElem::~PtnElem() {
2340 DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap
&patternMap
, dtStrEnum type
, UErrorCode
& status
) {
2342 PtnSkeleton
*curSkeleton
;
2347 fSkeletons
= new UVector(status
);
2348 if (U_FAILURE(status
)) {
2352 for (bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
2353 curElem
= patternMap
.boot
[bootIndex
];
2354 while (curElem
!=NULL
) {
2356 case DT_BASESKELETON
:
2357 s
=curElem
->basePattern
;
2363 curSkeleton
=curElem
->skeleton
;
2364 s
=curSkeleton
->getSkeleton();
2367 if ( !isCanonicalItem(s
) ) {
2368 fSkeletons
->addElement(new UnicodeString(s
), status
);
2369 if (U_FAILURE(status
)) {
2375 curElem
= curElem
->next
;
2378 if ((bootIndex
==MAX_PATTERN_ENTRIES
) && (curElem
!=NULL
) ) {
2379 status
= U_BUFFER_OVERFLOW_ERROR
;
2383 const UnicodeString
*
2384 DTSkeletonEnumeration::snext(UErrorCode
& status
) {
2385 if (U_SUCCESS(status
) && pos
< fSkeletons
->size()) {
2386 return (const UnicodeString
*)fSkeletons
->elementAt(pos
++);
2392 DTSkeletonEnumeration::reset(UErrorCode
& /*status*/) {
2397 DTSkeletonEnumeration::count(UErrorCode
& /*status*/) const {
2398 return (fSkeletons
==NULL
) ? 0 : fSkeletons
->size();
2402 DTSkeletonEnumeration::isCanonicalItem(const UnicodeString
& item
) {
2403 if ( item
.length() != 1 ) {
2406 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2407 if (item
.charAt(0)==Canonical_Items
[i
]) {
2414 DTSkeletonEnumeration::~DTSkeletonEnumeration() {
2416 for (int32_t i
=0; i
<fSkeletons
->size(); ++i
) {
2417 if ((s
=(UnicodeString
*)fSkeletons
->elementAt(i
))!=NULL
) {
2424 DTRedundantEnumeration::DTRedundantEnumeration() {
2430 DTRedundantEnumeration::add(const UnicodeString
& pattern
, UErrorCode
& status
) {
2431 if (U_FAILURE(status
)) return;
2432 if (fPatterns
== NULL
) {
2433 fPatterns
= new UVector(status
);
2434 if (U_FAILURE(status
)) {
2440 fPatterns
->addElement(new UnicodeString(pattern
), status
);
2441 if (U_FAILURE(status
)) {
2448 const UnicodeString
*
2449 DTRedundantEnumeration::snext(UErrorCode
& status
) {
2450 if (U_SUCCESS(status
) && pos
< fPatterns
->size()) {
2451 return (const UnicodeString
*)fPatterns
->elementAt(pos
++);
2457 DTRedundantEnumeration::reset(UErrorCode
& /*status*/) {
2462 DTRedundantEnumeration::count(UErrorCode
& /*status*/) const {
2463 return (fPatterns
==NULL
) ? 0 : fPatterns
->size();
2467 DTRedundantEnumeration::isCanonicalItem(const UnicodeString
& item
) {
2468 if ( item
.length() != 1 ) {
2471 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2472 if (item
.charAt(0)==Canonical_Items
[i
]) {
2479 DTRedundantEnumeration::~DTRedundantEnumeration() {
2481 for (int32_t i
=0; i
<fPatterns
->size(); ++i
) {
2482 if ((s
=(UnicodeString
*)fPatterns
->elementAt(i
))!=NULL
) {
2492 #endif /* #if !UCONFIG_NO_FORMATTING */