1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
11 *******************************************************************************
14 #include "unicode/utypes.h"
15 #if !UCONFIG_NO_FORMATTING
17 #include "unicode/datefmt.h"
18 #include "unicode/decimfmt.h"
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/dtptngen.h"
21 #include "unicode/simpleformatter.h"
22 #include "unicode/smpdtfmt.h"
23 #include "unicode/udat.h"
24 #include "unicode/udatpg.h"
25 #include "unicode/uniset.h"
26 #include "unicode/uloc.h"
27 #include "unicode/ures.h"
28 #include "unicode/ustring.h"
29 #include "unicode/rep.h"
39 #include "dtptngen_impl.h"
44 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
46 * If we are on EBCDIC, use an iterator which will
47 * traverse the bundles in ASCII order.
49 #define U_USE_ASCII_BUNDLE_ITERATOR
50 #define U_SORT_ASCII_BUNDLE_ITERATOR
53 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
55 #include "unicode/ustring.h"
60 UResourceBundle
*item
;
63 struct UResourceBundleAIterator
{
64 UResourceBundle
*bund
;
70 /* Must be C linkage to pass function pointer to the sort function */
74 static int32_t U_CALLCONV
75 ures_a_codepointSort(const void *context
, const void *left
, const void *right
) {
76 //CompareContext *cmp=(CompareContext *)context;
77 return u_strcmp(((const UResAEntry
*)left
)->key
,
78 ((const UResAEntry
*)right
)->key
);
83 static void ures_a_open(UResourceBundleAIterator
*aiter
, UResourceBundle
*bund
, UErrorCode
*status
) {
84 if(U_FAILURE(*status
)) {
88 aiter
->num
= ures_getSize(aiter
->bund
);
90 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
91 aiter
->entries
= NULL
;
93 aiter
->entries
= (UResAEntry
*)uprv_malloc(sizeof(UResAEntry
)*aiter
->num
);
94 for(int i
=0;i
<aiter
->num
;i
++) {
95 aiter
->entries
[i
].item
= ures_getByIndex(aiter
->bund
, i
, NULL
, status
);
96 const char *akey
= ures_getKey(aiter
->entries
[i
].item
);
97 int32_t len
= uprv_strlen(akey
)+1;
98 aiter
->entries
[i
].key
= (UChar
*)uprv_malloc(len
*sizeof(UChar
));
99 u_charsToUChars(akey
, aiter
->entries
[i
].key
, len
);
101 uprv_sortArray(aiter
->entries
, aiter
->num
, sizeof(UResAEntry
), ures_a_codepointSort
, NULL
, TRUE
, status
);
105 static void ures_a_close(UResourceBundleAIterator
*aiter
) {
106 #if defined(U_SORT_ASCII_BUNDLE_ITERATOR)
107 for(int i
=0;i
<aiter
->num
;i
++) {
108 uprv_free(aiter
->entries
[i
].key
);
109 ures_close(aiter
->entries
[i
].item
);
114 static const UChar
*ures_a_getNextString(UResourceBundleAIterator
*aiter
, int32_t *len
, const char **key
, UErrorCode
*err
) {
115 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
116 return ures_getNextString(aiter
->bund
, len
, key
, err
);
118 if(U_FAILURE(*err
)) return NULL
;
119 UResourceBundle
*item
= aiter
->entries
[aiter
->cursor
].item
;
120 const UChar
* ret
= ures_getString(item
, len
, err
);
121 *key
= ures_getKey(item
);
133 // *****************************************************************************
134 // class DateTimePatternGenerator
135 // *****************************************************************************
136 static const UChar Canonical_Items
[] = {
138 CAP_G
, LOW_Y
, CAP_Q
, CAP_M
, LOW_W
, CAP_W
, CAP_E
,
139 CAP_D
, CAP_F
, LOW_D
, LOW_A
, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
140 CAP_H
, LOW_M
, LOW_S
, CAP_S
, LOW_V
, 0
143 static const dtTypeElem dtTypes
[] = {
144 // patternChar, field, type, minLen, weight
145 {CAP_G
, UDATPG_ERA_FIELD
, DT_SHORT
, 1, 3,},
146 {CAP_G
, UDATPG_ERA_FIELD
, DT_LONG
, 4, 0},
147 {CAP_G
, UDATPG_ERA_FIELD
, DT_NARROW
, 5, 0},
149 {LOW_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
, 1, 20},
150 {CAP_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20},
151 {LOW_U
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 20},
152 {LOW_R
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 3*DT_DELTA
, 1, 20},
153 {CAP_U
, UDATPG_YEAR_FIELD
, DT_SHORT
, 1, 3},
154 {CAP_U
, UDATPG_YEAR_FIELD
, DT_LONG
, 4, 0},
155 {CAP_U
, UDATPG_YEAR_FIELD
, DT_NARROW
, 5, 0},
157 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
, 1, 2},
158 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
, 3, 0},
159 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
, 4, 0},
160 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NARROW
, 5, 0},
161 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
162 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
163 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
164 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
166 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NUMERIC
, 1, 2},
167 {CAP_M
, UDATPG_MONTH_FIELD
, DT_SHORT
, 3, 0},
168 {CAP_M
, UDATPG_MONTH_FIELD
, DT_LONG
, 4, 0},
169 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NARROW
, 5, 0},
170 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
171 {CAP_L
, UDATPG_MONTH_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
172 {CAP_L
, UDATPG_MONTH_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
173 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
174 {LOW_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1},
176 {LOW_W
, UDATPG_WEEK_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 2},
178 {CAP_W
, UDATPG_WEEK_OF_MONTH_FIELD
, DT_NUMERIC
, 1, 0},
180 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
, 1, 3},
181 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
, 4, 0},
182 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
, 5, 0},
183 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
, 6, 0},
184 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 2},
185 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- 2*DT_DELTA
, 3, 0},
186 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
187 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- 2*DT_DELTA
, 5, 0},
188 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
- 2*DT_DELTA
, 6, 0},
189 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical
190 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
191 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
192 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
193 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
- DT_DELTA
, 6, 0},
195 {LOW_D
, UDATPG_DAY_FIELD
, DT_NUMERIC
, 1, 2},
196 {LOW_G
, UDATPG_DAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20}, // really internal use, so we don't care
198 {CAP_D
, UDATPG_DAY_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 3},
200 {CAP_F
, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD
, DT_NUMERIC
, 1, 0},
202 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
, 1, 3},
203 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
, 4, 0},
204 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
, 5, 0},
205 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
- DT_DELTA
, 1, 3},
206 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
207 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
208 // b needs to be closer to a than to B, so we make this 3*DT_DELTA
209 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
- 3*DT_DELTA
, 1, 3},
210 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
- 3*DT_DELTA
, 4, 0},
211 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
- 3*DT_DELTA
, 5, 0},
213 {CAP_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 10*DT_DELTA
, 1, 2}, // 24 hour
214 {LOW_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 11*DT_DELTA
, 1, 2}, // 24 hour
215 {LOW_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
, 1, 2}, // 12 hour
216 {CAP_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // 12 hour
217 // The C code has had versions of the following 3, keep & update. Should not need these, but...
218 // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns
219 // get skipped instead of mapped to the right hour chars, for example in
220 // DateFormatTest::TestPatternFromSkeleton
221 // IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton
222 // DateIntervalFormatTest::testTicket11985
223 // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton.
224 {CAP_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 5*DT_DELTA
, 1, 2}, // 12/24 hour no AM/PM
225 {LOW_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 6*DT_DELTA
, 1, 6}, // 12/24 hour
226 {CAP_C
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 7*DT_DELTA
, 1, 6}, // 12/24 hour with preferred dayPeriods for 12
228 {LOW_M
, UDATPG_MINUTE_FIELD
, DT_NUMERIC
, 1, 2},
230 {LOW_S
, UDATPG_SECOND_FIELD
, DT_NUMERIC
, 1, 2},
231 {CAP_A
, UDATPG_SECOND_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1000},
233 {CAP_S
, UDATPG_FRACTIONAL_SECOND_FIELD
, DT_NUMERIC
, 1, 1000},
235 {LOW_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- 2*DT_DELTA
, 1, 0},
236 {LOW_V
, UDATPG_ZONE_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
237 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
, 1, 3},
238 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_LONG
, 4, 0},
239 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 3},
240 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
241 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 5, 0},
242 {CAP_O
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
243 {CAP_O
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
244 {CAP_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
245 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 2, 0},
246 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
-1 - DT_DELTA
, 3, 0},
247 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
-2 - DT_DELTA
, 4, 0},
248 {CAP_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
249 {CAP_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
250 {CAP_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
251 {LOW_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
252 {LOW_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
253 {LOW_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
255 {0, UDATPG_FIELD_COUNT
, 0, 0, 0} , // last row of dtTypes[]
258 static const char* const CLDR_FIELD_APPEND
[] = {
259 "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week",
260 "*", "*", "Day", "*", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
261 "Hour", "Minute", "Second", "*", "Timezone"
264 static const char* const CLDR_FIELD_NAME
[UDATPG_FIELD_COUNT
] = {
265 "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday",
266 "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
267 "hour", "minute", "second", "*", "zone"
270 static const char* const CLDR_FIELD_WIDTH
[] = { // [UDATPG_WIDTH_COUNT]
271 "", "-short", "-narrow"
274 // TODO(ticket:13619): remove when definition uncommented in dtptngen.h.
275 static const int32_t UDATPG_WIDTH_COUNT
= UDATPG_NARROW
+ 1;
276 static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM
= UDATPG_WIDE
;
277 static constexpr int32_t UDATPG_FIELD_KEY_MAX
= 24; // max length of CLDR field tag (type + width)
280 static const UChar UDATPG_ItemFormat
[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
281 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524
283 //static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
285 static const char DT_DateTimePatternsTag
[]="DateTimePatterns";
286 static const char DT_DateTimeCalendarTag
[]="calendar";
287 static const char DT_DateTimeGregorianTag
[]="gregorian";
288 static const char DT_DateTimeAppendItemsTag
[]="appendItems";
289 static const char DT_DateTimeFieldsTag
[]="fields";
290 static const char DT_DateTimeAvailableFormatsTag
[]="availableFormats";
291 //static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);
293 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator
)
294 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration
)
295 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration
)
297 DateTimePatternGenerator
* U_EXPORT2
298 DateTimePatternGenerator::createInstance(UErrorCode
& status
) {
299 return createInstance(Locale::getDefault(), status
);
302 DateTimePatternGenerator
* U_EXPORT2
303 DateTimePatternGenerator::createInstance(const Locale
& locale
, UErrorCode
& status
) {
304 if (U_FAILURE(status
)) {
307 LocalPointer
<DateTimePatternGenerator
> result(
308 new DateTimePatternGenerator(locale
, status
), status
);
309 return U_SUCCESS(status
) ? result
.orphan() : NULL
;
312 DateTimePatternGenerator
* U_EXPORT2
313 DateTimePatternGenerator::createEmptyInstance(UErrorCode
& status
) {
314 DateTimePatternGenerator
*result
= new DateTimePatternGenerator(status
);
315 if (result
== NULL
) {
316 status
= U_MEMORY_ALLOCATION_ERROR
;
318 if (U_FAILURE(status
)) {
325 DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode
&status
) :
327 fAvailableFormatKeyHash(NULL
)
329 fp
= new FormatParser();
330 dtMatcher
= new DateTimeMatcher();
331 distanceInfo
= new DistanceInfo();
332 patternMap
= new PatternMap();
333 if (fp
== NULL
|| dtMatcher
== NULL
|| distanceInfo
== NULL
|| patternMap
== NULL
) {
334 status
= U_MEMORY_ALLOCATION_ERROR
;
338 DateTimePatternGenerator::DateTimePatternGenerator(const Locale
& locale
, UErrorCode
&status
) :
340 fAvailableFormatKeyHash(NULL
)
342 fp
= new FormatParser();
343 dtMatcher
= new DateTimeMatcher();
344 distanceInfo
= new DistanceInfo();
345 patternMap
= new PatternMap();
346 if (fp
== NULL
|| dtMatcher
== NULL
|| distanceInfo
== NULL
|| patternMap
== NULL
) {
347 status
= U_MEMORY_ALLOCATION_ERROR
;
350 initData(locale
, status
);
354 DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator
& other
) :
357 fAvailableFormatKeyHash(NULL
)
359 fp
= new FormatParser();
360 dtMatcher
= new DateTimeMatcher();
361 distanceInfo
= new DistanceInfo();
362 patternMap
= new PatternMap();
366 DateTimePatternGenerator
&
367 DateTimePatternGenerator::operator=(const DateTimePatternGenerator
& other
) {
369 if (&other
== this) {
372 pLocale
= other
.pLocale
;
373 fDefaultHourFormatChar
= other
.fDefaultHourFormatChar
;
375 dtMatcher
->copyFrom(other
.dtMatcher
->skeleton
);
376 *distanceInfo
= *(other
.distanceInfo
);
377 dateTimeFormat
= other
.dateTimeFormat
;
378 decimal
= other
.decimal
;
379 // NUL-terminate for the C API.
380 dateTimeFormat
.getTerminatedBuffer();
381 decimal
.getTerminatedBuffer();
383 if ( other
.skipMatcher
== NULL
) {
387 skipMatcher
= new DateTimeMatcher(*other
.skipMatcher
);
389 for (int32_t i
=0; i
< UDATPG_FIELD_COUNT
; ++i
) {
390 appendItemFormats
[i
] = other
.appendItemFormats
[i
];
391 appendItemFormats
[i
].getTerminatedBuffer(); // NUL-terminate for the C API.
392 for (int32_t j
=0; j
< UDATPG_WIDTH_COUNT
; ++j
) {
393 fieldDisplayNames
[i
][j
] = other
.fieldDisplayNames
[i
][j
];
394 fieldDisplayNames
[i
][j
].getTerminatedBuffer(); // NUL-terminate for the C API.
397 UErrorCode status
= U_ZERO_ERROR
;
398 patternMap
->copyFrom(*other
.patternMap
, status
);
399 copyHashtable(other
.fAvailableFormatKeyHash
, status
);
405 DateTimePatternGenerator::operator==(const DateTimePatternGenerator
& other
) const {
406 if (this == &other
) {
409 if ((pLocale
==other
.pLocale
) && (patternMap
->equals(*other
.patternMap
)) &&
410 (dateTimeFormat
==other
.dateTimeFormat
) && (decimal
==other
.decimal
)) {
411 for ( int32_t i
=0 ; i
<UDATPG_FIELD_COUNT
; ++i
) {
412 if (appendItemFormats
[i
] != other
.appendItemFormats
[i
]) {
415 for (int32_t j
=0; j
< UDATPG_WIDTH_COUNT
; ++j
) {
416 if (fieldDisplayNames
[i
][j
] != other
.fieldDisplayNames
[i
][j
]) {
429 DateTimePatternGenerator::operator!=(const DateTimePatternGenerator
& other
) const {
430 return !operator==(other
);
433 DateTimePatternGenerator::~DateTimePatternGenerator() {
434 if (fAvailableFormatKeyHash
!=NULL
) {
435 delete fAvailableFormatKeyHash
;
438 if (fp
!= NULL
) delete fp
;
439 if (dtMatcher
!= NULL
) delete dtMatcher
;
440 if (distanceInfo
!= NULL
) delete distanceInfo
;
441 if (patternMap
!= NULL
) delete patternMap
;
442 if (skipMatcher
!= NULL
) delete skipMatcher
;
447 UInitOnce initOnce
= U_INITONCE_INITIALIZER
;
448 UHashtable
*localeToAllowedHourFormatsMap
= NULL
;
450 // Value deleter for hashmap.
451 U_CFUNC
void U_CALLCONV
deleteAllowedHourFormats(void *ptr
) {
455 // Close hashmap at cleanup.
456 U_CFUNC UBool U_CALLCONV
allowedHourFormatsCleanup() {
457 uhash_close(localeToAllowedHourFormatsMap
);
461 enum AllowedHourFormat
{
462 ALLOWED_HOUR_FORMAT_UNKNOWN
= -1,
463 ALLOWED_HOUR_FORMAT_h
,
464 ALLOWED_HOUR_FORMAT_H
,
465 ALLOWED_HOUR_FORMAT_hb
,
466 ALLOWED_HOUR_FORMAT_Hb
,
467 ALLOWED_HOUR_FORMAT_hB
,
468 ALLOWED_HOUR_FORMAT_HB
474 DateTimePatternGenerator::initData(const Locale
& locale
, UErrorCode
&status
) {
475 //const char *baseLangName = locale.getBaseName(); // unused
478 fAvailableFormatKeyHash
=NULL
;
479 addCanonicalItems(status
);
480 addICUPatterns(locale
, status
);
481 addCLDRData(locale
, status
);
482 setDateTimeFromCalendar(locale
, status
);
483 setDecimalSymbols(locale
, status
);
484 umtx_initOnce(initOnce
, loadAllowedHourFormatsData
, status
);
485 getAllowedHourFormats(locale
, status
);
486 } // DateTimePatternGenerator::initData
490 struct AllowedHourFormatsSink
: public ResourceSink
{
491 // Initialize sub-sinks.
492 AllowedHourFormatsSink() {}
493 virtual ~AllowedHourFormatsSink();
495 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
496 UErrorCode
&errorCode
) {
497 ResourceTable timeData
= value
.getTable(errorCode
);
498 if (U_FAILURE(errorCode
)) { return; }
499 for (int32_t i
= 0; timeData
.getKeyAndValue(i
, key
, value
); ++i
) {
500 const char *regionOrLocale
= key
;
501 ResourceTable formatList
= value
.getTable(errorCode
);
502 if (U_FAILURE(errorCode
)) { return; }
503 for (int32_t j
= 0; formatList
.getKeyAndValue(j
, key
, value
); ++j
) {
504 if (uprv_strcmp(key
, "allowed") == 0) { // Ignore "preferred" list.
505 LocalMemory
<int32_t> list
;
507 if (value
.getType() == URES_STRING
) {
508 if (list
.allocateInsteadAndReset(2) == NULL
) {
509 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
512 list
[0] = getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
516 ResourceArray allowedFormats
= value
.getArray(errorCode
);
517 length
= allowedFormats
.getSize();
518 if (list
.allocateInsteadAndReset(length
+ 1) == NULL
) {
519 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
522 for (int32_t k
= 0; k
< length
; ++k
) {
523 allowedFormats
.getValue(k
, value
);
524 list
[k
] = getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
527 list
[length
] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
528 uhash_put(localeToAllowedHourFormatsMap
,
529 const_cast<char *>(regionOrLocale
), list
.orphan(), &errorCode
);
530 if (U_FAILURE(errorCode
)) { return; }
536 AllowedHourFormat
getHourFormatFromUnicodeString(const UnicodeString
&s
) {
537 if (s
.length() == 1) {
538 if (s
[0] == LOW_H
) { return ALLOWED_HOUR_FORMAT_h
; }
539 if (s
[0] == CAP_H
) { return ALLOWED_HOUR_FORMAT_H
; }
540 } else if (s
.length() == 2) {
541 if (s
[0] == LOW_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_hb
; }
542 if (s
[0] == CAP_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_Hb
; }
543 if (s
[0] == LOW_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_hB
; }
544 if (s
[0] == CAP_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_HB
; }
547 return ALLOWED_HOUR_FORMAT_UNKNOWN
;
553 AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
555 U_CFUNC
void U_CALLCONV
DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode
&status
) {
556 if (U_FAILURE(status
)) { return; }
557 localeToAllowedHourFormatsMap
= uhash_open(
558 uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
559 uhash_setValueDeleter(localeToAllowedHourFormatsMap
, deleteAllowedHourFormats
);
560 LocalUResourceBundlePointer
rb(ures_openDirect(NULL
, "supplementalData", &status
));
562 AllowedHourFormatsSink sink
;
563 // TODO: Currently in the enumeration each table allocates a new array.
564 // Try to reduce the number of memory allocations. Consider storing a
565 // UVector32 with the concatenation of all of the sub-arrays, put the start index
566 // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
567 // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
568 // object. Remember to clean up the vector, too.
569 ures_getAllItemsWithFallback(rb
.getAlias(), "timeData", sink
, status
);
571 ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS
, allowedHourFormatsCleanup
);
574 void DateTimePatternGenerator::getAllowedHourFormats(const Locale
&locale
, UErrorCode
&status
) {
575 if (U_FAILURE(status
)) { return; }
576 const char *localeID
= locale
.getName();
577 char maxLocaleID
[ULOC_FULLNAME_CAPACITY
];
578 int32_t length
= uloc_addLikelySubtags(localeID
, maxLocaleID
, ULOC_FULLNAME_CAPACITY
, &status
);
579 if (U_FAILURE(status
)) {
581 } else if (length
== ULOC_FULLNAME_CAPACITY
) { // no room for NUL
582 status
= U_BUFFER_OVERFLOW_ERROR
;
585 Locale maxLocale
= Locale(maxLocaleID
);
587 const char *country
= maxLocale
.getCountry();
588 if (*country
== '\0') { country
= "001"; }
589 const char *language
= maxLocale
.getLanguage();
591 CharString langCountry
;
592 langCountry
.append(language
, uprv_strlen(language
), status
);
593 langCountry
.append('_', status
);
594 langCountry
.append(country
, uprv_strlen(country
), status
);
596 int32_t *allowedFormats
;
597 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, langCountry
.data());
598 if (allowedFormats
== NULL
) {
599 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, const_cast<char *>(country
));
602 if (allowedFormats
!= NULL
) { // Lookup is successful
603 for (int32_t i
= 0; i
< UPRV_LENGTHOF(fAllowedHourFormats
); ++i
) {
604 fAllowedHourFormats
[i
] = allowedFormats
[i
];
605 if (allowedFormats
[i
] == ALLOWED_HOUR_FORMAT_UNKNOWN
) {
609 } else { // Lookup failed, twice
610 fAllowedHourFormats
[0] = ALLOWED_HOUR_FORMAT_H
;
611 fAllowedHourFormats
[1] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
616 DateTimePatternGenerator::getSkeleton(const UnicodeString
& pattern
, UErrorCode
&
619 DateTimeMatcher matcher
;
620 PtnSkeleton localSkeleton
;
621 matcher
.set(pattern
, &fp
, localSkeleton
);
622 return localSkeleton
.getSkeleton();
626 DateTimePatternGenerator::staticGetSkeleton(
627 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
629 DateTimeMatcher matcher
;
630 PtnSkeleton localSkeleton
;
631 matcher
.set(pattern
, &fp
, localSkeleton
);
632 return localSkeleton
.getSkeleton();
636 DateTimePatternGenerator::getBaseSkeleton(const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
638 DateTimeMatcher matcher
;
639 PtnSkeleton localSkeleton
;
640 matcher
.set(pattern
, &fp
, localSkeleton
);
641 return localSkeleton
.getBaseSkeleton();
645 DateTimePatternGenerator::staticGetBaseSkeleton(
646 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
648 DateTimeMatcher matcher
;
649 PtnSkeleton localSkeleton
;
650 matcher
.set(pattern
, &fp
, localSkeleton
);
651 return localSkeleton
.getBaseSkeleton();
655 DateTimePatternGenerator::addICUPatterns(const Locale
& locale
, UErrorCode
& status
) {
656 if (U_FAILURE(status
)) { return; }
657 UnicodeString dfPattern
;
658 UnicodeString conflictingString
;
661 // Load with ICU patterns
662 for (int32_t i
=DateFormat::kFull
; i
<=DateFormat::kShort
; i
++) {
663 DateFormat::EStyle style
= (DateFormat::EStyle
)i
;
664 df
= DateFormat::createDateInstance(style
, locale
);
665 SimpleDateFormat
* sdf
;
666 if (df
!= NULL
&& (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != NULL
) {
667 sdf
->toPattern(dfPattern
);
668 addPattern(dfPattern
, FALSE
, conflictingString
, status
);
670 // TODO Maybe we should return an error when the date format isn't simple.
672 if (U_FAILURE(status
)) { return; }
674 df
= DateFormat::createTimeInstance(style
, locale
);
675 if (df
!= NULL
&& (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != NULL
) {
676 sdf
->toPattern(dfPattern
);
677 addPattern(dfPattern
, FALSE
, conflictingString
, status
);
679 // TODO: C++ and Java are inconsistent (see #12568).
680 // C++ uses MEDIUM, but Java uses SHORT.
681 if ( i
==DateFormat::kShort
&& !dfPattern
.isEmpty() ) {
682 consumeShortTimePattern(dfPattern
, status
);
685 // TODO Maybe we should return an error when the date format isn't simple.
687 if (U_FAILURE(status
)) { return; }
692 DateTimePatternGenerator::hackTimes(const UnicodeString
& hackPattern
, UErrorCode
& status
) {
693 UnicodeString conflictingString
;
695 fp
->set(hackPattern
);
698 for (int32_t i
=0; i
<fp
->itemNumber
; ++i
) {
699 UnicodeString field
= fp
->items
[i
];
700 if ( fp
->isQuoteLiteral(field
) ) {
702 UnicodeString quoteLiteral
;
703 fp
->getQuoteLiteral(quoteLiteral
, &i
);
704 mmss
+= quoteLiteral
;
708 if (fp
->isPatternSeparator(field
) && gotMm
) {
712 UChar ch
=field
.charAt(0);
723 addPattern(mmss
, FALSE
, conflictingString
, status
);
727 if (gotMm
|| ch
==LOW_Z
|| ch
==CAP_Z
|| ch
==LOW_V
|| ch
==CAP_V
) {
737 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
739 static const UChar hourFormatChars
[] = { CAP_H
, LOW_H
, CAP_K
, LOW_K
, 0 }; // HhKk, the hour format characters
742 DateTimePatternGenerator::getCalendarTypeToUse(const Locale
& locale
, CharString
& destination
, UErrorCode
& err
) {
743 destination
.clear().append(DT_DateTimeGregorianTag
, -1, err
); // initial default
744 if ( U_SUCCESS(err
) ) {
745 char localeWithCalendarKey
[ULOC_LOCALE_IDENTIFIER_CAPACITY
];
746 // obtain a locale that always has the calendar key value that should be used
747 ures_getFunctionalEquivalent(
748 localeWithCalendarKey
,
749 ULOC_LOCALE_IDENTIFIER_CAPACITY
,
757 localeWithCalendarKey
[ULOC_LOCALE_IDENTIFIER_CAPACITY
-1] = 0; // ensure null termination
758 // now get the calendar key value from that locale
759 char calendarType
[ULOC_KEYWORDS_CAPACITY
];
760 int32_t calendarTypeLen
= uloc_getKeywordValue(
761 localeWithCalendarKey
,
764 ULOC_KEYWORDS_CAPACITY
,
766 if (U_SUCCESS(err
) && calendarTypeLen
< ULOC_KEYWORDS_CAPACITY
) {
767 destination
.clear().append(calendarType
, -1, err
);
768 if (U_FAILURE(err
)) { return; }
775 DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString
& shortTimePattern
,
776 UErrorCode
& status
) {
778 // set fDefaultHourFormatChar to the hour format character from this pattern
779 int32_t tfIdx
, tfLen
= shortTimePattern
.length();
780 UBool ignoreChars
= FALSE
;
781 for (tfIdx
= 0; tfIdx
< tfLen
; tfIdx
++) {
782 UChar tfChar
= shortTimePattern
.charAt(tfIdx
);
783 if ( tfChar
== SINGLE_QUOTE
) {
784 ignoreChars
= !ignoreChars
; // toggle (handle quoted literals & '' for single quote)
785 } else if ( !ignoreChars
&& u_strchr(hourFormatChars
, tfChar
) != NULL
) {
786 fDefaultHourFormatChar
= tfChar
;
792 hackTimes(shortTimePattern
, status
);
795 struct DateTimePatternGenerator::AppendItemFormatsSink
: public ResourceSink
{
797 // Destination for data, modified via setters.
798 DateTimePatternGenerator
& dtpg
;
800 AppendItemFormatsSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
801 virtual ~AppendItemFormatsSink();
803 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
804 UErrorCode
&errorCode
) {
805 ResourceTable itemsTable
= value
.getTable(errorCode
);
806 if (U_FAILURE(errorCode
)) { return; }
807 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
808 UDateTimePatternField field
= dtpg
.getAppendFormatNumber(key
);
809 if (field
== UDATPG_FIELD_COUNT
) { continue; }
810 const UnicodeString
& valueStr
= value
.getUnicodeString(errorCode
);
811 if (dtpg
.getAppendItemFormat(field
).isEmpty() && !valueStr
.isEmpty()) {
812 dtpg
.setAppendItemFormat(field
, valueStr
);
817 void fillInMissing() {
818 UnicodeString
defaultItemFormat(TRUE
, UDATPG_ItemFormat
, UPRV_LENGTHOF(UDATPG_ItemFormat
)-1); // Read-only alias.
819 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; i
++) {
820 UDateTimePatternField field
= (UDateTimePatternField
)i
;
821 if (dtpg
.getAppendItemFormat(field
).isEmpty()) {
822 dtpg
.setAppendItemFormat(field
, defaultItemFormat
);
828 struct DateTimePatternGenerator::AppendItemNamesSink
: public ResourceSink
{
830 // Destination for data, modified via setters.
831 DateTimePatternGenerator
& dtpg
;
833 AppendItemNamesSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
834 virtual ~AppendItemNamesSink();
836 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
837 UErrorCode
&errorCode
) {
838 ResourceTable itemsTable
= value
.getTable(errorCode
);
839 if (U_FAILURE(errorCode
)) { return; }
840 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
841 UDateTimePGDisplayWidth width
;
842 UDateTimePatternField field
= dtpg
.getFieldAndWidthIndices(key
, &width
);
843 if (field
== UDATPG_FIELD_COUNT
) { continue; }
844 ResourceTable detailsTable
= value
.getTable(errorCode
);
845 if (U_FAILURE(errorCode
)) { return; }
846 for (int32_t j
= 0; detailsTable
.getKeyAndValue(j
, key
, value
); ++j
) {
847 if (uprv_strcmp(key
, "dn") != 0) { continue; }
848 const UnicodeString
& valueStr
= value
.getUnicodeString(errorCode
);
849 if (dtpg
.getFieldDisplayName(field
,width
).isEmpty() && !valueStr
.isEmpty()) {
850 dtpg
.setFieldDisplayName(field
,width
,valueStr
);
857 void fillInMissing() {
858 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; i
++) {
859 UnicodeString
& valueStr
= dtpg
.getMutableFieldDisplayName((UDateTimePatternField
)i
, UDATPG_WIDE
);
860 if (valueStr
.isEmpty()) {
865 valueStr
+= (UChar
)(i
+0x30);
868 valueStr
+= (UChar
)0x31;
869 valueStr
+= (UChar
)(i
-10 + 0x30);
871 // NUL-terminate for the C API.
872 valueStr
.getTerminatedBuffer();
874 for (int32_t j
= 1; j
< UDATPG_WIDTH_COUNT
; j
++) {
875 UnicodeString
& valueStr
= dtpg
.getMutableFieldDisplayName((UDateTimePatternField
)i
, (UDateTimePGDisplayWidth
)j
);
876 if (valueStr
.isEmpty()) {
877 valueStr
= dtpg
.getFieldDisplayName((UDateTimePatternField
)i
, (UDateTimePGDisplayWidth
)(j
-1));
884 struct DateTimePatternGenerator::AvailableFormatsSink
: public ResourceSink
{
886 // Destination for data, modified via setters.
887 DateTimePatternGenerator
& dtpg
;
889 // Temporary variable, required for calling addPatternWithSkeleton.
890 UnicodeString conflictingPattern
;
892 AvailableFormatsSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
893 virtual ~AvailableFormatsSink();
895 virtual void put(const char *key
, ResourceValue
&value
, UBool isRoot
,
896 UErrorCode
&errorCode
) {
897 ResourceTable itemsTable
= value
.getTable(errorCode
);
898 if (U_FAILURE(errorCode
)) { return; }
899 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
900 const UnicodeString
formatKey(key
, -1, US_INV
);
901 if (!dtpg
.isAvailableFormatSet(formatKey
) ) {
902 dtpg
.setAvailableFormat(formatKey
, errorCode
);
903 // Add pattern with its associated skeleton. Override any duplicate
904 // derived from std patterns, but not a previous availableFormats entry:
905 const UnicodeString
& formatValue
= value
.getUnicodeString(errorCode
);
906 conflictingPattern
.remove();
907 dtpg
.addPatternWithSkeleton(formatValue
, &formatKey
, !isRoot
, conflictingPattern
, errorCode
);
913 // Virtual destructors must be defined out of line.
914 DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
915 DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
916 DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}
919 DateTimePatternGenerator::addCLDRData(const Locale
& locale
, UErrorCode
& errorCode
) {
920 if (U_FAILURE(errorCode
)) { return; }
921 UnicodeString rbPattern
, value
, field
;
924 LocalUResourceBundlePointer
rb(ures_open(NULL
, locale
.getName(), &errorCode
));
925 if (U_FAILURE(errorCode
)) { return; }
927 CharString calendarTypeToUse
; // to be filled in with the type to use, if all goes well
928 getCalendarTypeToUse(locale
, calendarTypeToUse
, errorCode
);
929 if (U_FAILURE(errorCode
)) { return; }
931 // Local err to ignore resource not found exceptions
932 UErrorCode err
= U_ZERO_ERROR
;
934 // Load append item formats.
935 AppendItemFormatsSink
appendItemFormatsSink(*this);
937 .append(DT_DateTimeCalendarTag
, errorCode
)
938 .append('/', errorCode
)
939 .append(calendarTypeToUse
, errorCode
)
940 .append('/', errorCode
)
941 .append(DT_DateTimeAppendItemsTag
, errorCode
); // i.e., calendar/xxx/appendItems
942 if (U_FAILURE(errorCode
)) { return; }
943 ures_getAllItemsWithFallback(rb
.getAlias(), path
.data(), appendItemFormatsSink
, err
);
944 appendItemFormatsSink
.fillInMissing();
946 // Load CLDR item names.
948 AppendItemNamesSink
appendItemNamesSink(*this);
949 ures_getAllItemsWithFallback(rb
.getAlias(), DT_DateTimeFieldsTag
, appendItemNamesSink
, err
);
950 appendItemNamesSink
.fillInMissing();
952 // Load the available formats from CLDR.
954 initHashtable(errorCode
);
955 if (U_FAILURE(errorCode
)) { return; }
956 AvailableFormatsSink
availableFormatsSink(*this);
958 .append(DT_DateTimeCalendarTag
, errorCode
)
959 .append('/', errorCode
)
960 .append(calendarTypeToUse
, errorCode
)
961 .append('/', errorCode
)
962 .append(DT_DateTimeAvailableFormatsTag
, errorCode
); // i.e., calendar/xxx/availableFormats
963 if (U_FAILURE(errorCode
)) { return; }
964 ures_getAllItemsWithFallback(rb
.getAlias(), path
.data(), availableFormatsSink
, err
);
968 DateTimePatternGenerator::initHashtable(UErrorCode
& err
) {
969 if (fAvailableFormatKeyHash
!=NULL
) {
972 if ((fAvailableFormatKeyHash
= new Hashtable(FALSE
, err
))==NULL
) {
973 err
=U_MEMORY_ALLOCATION_ERROR
;
979 DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field
, const UnicodeString
& value
) {
980 appendItemFormats
[field
] = value
;
981 // NUL-terminate for the C API.
982 appendItemFormats
[field
].getTerminatedBuffer();
986 DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field
) const {
987 return appendItemFormats
[field
];
991 DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field
, const UnicodeString
& value
) {
992 setFieldDisplayName(field
, UDATPG_WIDTH_APPENDITEM
, value
);
996 DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field
) const {
997 return fieldDisplayNames
[field
][UDATPG_WIDTH_APPENDITEM
];
1001 DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
, const UnicodeString
& value
) {
1002 fieldDisplayNames
[field
][width
] = value
;
1003 // NUL-terminate for the C API.
1004 fieldDisplayNames
[field
][width
].getTerminatedBuffer();
1008 DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
) const {
1009 return fieldDisplayNames
[field
][width
];
1013 DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
) {
1014 return fieldDisplayNames
[field
][width
];
1018 DateTimePatternGenerator::getAppendName(UDateTimePatternField field
, UnicodeString
& value
) {
1019 value
= SINGLE_QUOTE
;
1020 value
+= fieldDisplayNames
[field
][UDATPG_WIDTH_APPENDITEM
];
1021 value
+= SINGLE_QUOTE
;
1025 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UErrorCode
& status
) {
1026 return getBestPattern(patternForm
, UDATPG_MATCH_NO_OPTIONS
, status
);
1030 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
1031 const UnicodeString
*bestPattern
=NULL
;
1032 UnicodeString dtFormat
;
1033 UnicodeString resultPattern
;
1034 int32_t flags
= kDTPGNoFlags
;
1036 int32_t dateMask
=(1<<UDATPG_DAYPERIOD_FIELD
) - 1;
1037 int32_t timeMask
=(1<<UDATPG_FIELD_COUNT
) - 1 - dateMask
;
1039 // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
1040 UnicodeString patternFormMapped
= mapSkeletonMetacharacters(patternForm
, &flags
, options
, status
);
1041 if (U_FAILURE(status
)) {
1042 return UnicodeString();
1045 resultPattern
.remove();
1046 dtMatcher
->set(patternFormMapped
, 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 * Map a skeleton that may have metacharacters jJC to one without, by replacing
1077 * the metacharacters with locale-appropriate fields of of h/H/k/K and of a/b/B
1078 * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in
1079 * turn depends on initData having been run). This method also updates the flags
1080 * as necessary. Returns the updated skeleton.
1083 DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString
& patternForm
, int32_t* flags
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
1084 UnicodeString patternFormMapped
;
1085 patternFormMapped
.remove();
1086 UChar hourFormatSkeletonCharForLowJ
= fDefaultHourFormatChar
;
1087 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1088 case UADATPG_FORCE_12_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= LOW_H
; break;
1089 case UADATPG_FORCE_24_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= CAP_H
; break;
1092 UBool inQuoted
= FALSE
;
1093 int32_t patPos
, patLen
= patternForm
.length();
1094 for (patPos
= 0; patPos
< patLen
; patPos
++) {
1095 UChar patChr
= patternForm
.charAt(patPos
);
1096 if (patChr
== SINGLE_QUOTE
) {
1097 inQuoted
= !inQuoted
;
1098 } else if (!inQuoted
) {
1099 // Handle special mappings for 'j' and 'C' in which fields lengths
1100 // 1,3,5 => hour field length 1
1101 // 2,4,6 => hour field length 2
1102 // 1,2 => abbreviated dayPeriod (field length 1..3)
1103 // 3,4 => long dayPeriod (field length 4)
1104 // 5,6 => narrow dayPeriod (field length 5)
1105 if (patChr
== LOW_J
|| patChr
== CAP_C
) {
1106 int32_t extraLen
= 0; // 1 less than total field length
1107 while (patPos
+1 < patLen
&& patternForm
.charAt(patPos
+1)==patChr
) {
1111 int32_t hourLen
= 1 + (extraLen
& 1);
1112 int32_t dayPeriodLen
= (extraLen
< 2)? 1: 3 + (extraLen
>> 1);
1113 UChar hourChar
= LOW_H
;
1114 UChar dayPeriodChar
= LOW_A
;
1115 if (patChr
== LOW_J
) {
1116 hourChar
= hourFormatSkeletonCharForLowJ
;
1118 AllowedHourFormat preferred
;
1119 if (fAllowedHourFormats
[0] != ALLOWED_HOUR_FORMAT_UNKNOWN
) {
1120 preferred
= (AllowedHourFormat
)fAllowedHourFormats
[0];
1122 status
= U_INVALID_FORMAT_ERROR
;
1123 return UnicodeString();
1125 if (preferred
== ALLOWED_HOUR_FORMAT_H
|| preferred
== ALLOWED_HOUR_FORMAT_HB
|| preferred
== ALLOWED_HOUR_FORMAT_Hb
) {
1128 // in #13183 just add b/B to skeleton, no longer need to set special flags
1129 if (preferred
== ALLOWED_HOUR_FORMAT_HB
|| preferred
== ALLOWED_HOUR_FORMAT_hB
) {
1130 dayPeriodChar
= CAP_B
;
1131 } else if (preferred
== ALLOWED_HOUR_FORMAT_Hb
|| preferred
== ALLOWED_HOUR_FORMAT_hb
) {
1132 dayPeriodChar
= LOW_B
;
1135 if (hourChar
==CAP_H
|| hourChar
==LOW_K
) {
1138 while (dayPeriodLen
-- > 0) {
1139 patternFormMapped
.append(dayPeriodChar
);
1141 while (hourLen
-- > 0) {
1142 patternFormMapped
.append(hourChar
);
1144 } else if (patChr
== CAP_J
) {
1145 // Get pattern for skeleton with H, then replace H or k
1146 // with fDefaultHourFormatChar (if different)
1147 patternFormMapped
.append(CAP_H
);
1148 *flags
|= kDTPGSkeletonUsesCapJ
;
1150 patternFormMapped
.append(patChr
);
1154 return patternFormMapped
;
1158 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1159 const UnicodeString
& skeleton
,
1160 UErrorCode
& status
) {
1161 return replaceFieldTypes(pattern
, skeleton
, UDATPG_MATCH_NO_OPTIONS
, status
);
1165 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1166 const UnicodeString
& skeleton
,
1167 UDateTimePatternMatchOptions options
,
1168 UErrorCode
& /*status*/) {
1169 dtMatcher
->set(skeleton
, fp
);
1170 UnicodeString result
= adjustFieldTypes(pattern
, NULL
, kDTPGNoFlags
, options
);
1175 DateTimePatternGenerator::setDecimal(const UnicodeString
& newDecimal
) {
1176 this->decimal
= newDecimal
;
1177 // NUL-terminate for the C API.
1178 this->decimal
.getTerminatedBuffer();
1181 const UnicodeString
&
1182 DateTimePatternGenerator::getDecimal() const {
1187 DateTimePatternGenerator::addCanonicalItems(UErrorCode
& status
) {
1188 if (U_FAILURE(status
)) { return; }
1189 UnicodeString conflictingPattern
;
1191 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; i
++) {
1192 if (Canonical_Items
[i
] > 0) {
1193 addPattern(UnicodeString(Canonical_Items
[i
]), FALSE
, conflictingPattern
, status
);
1195 if (U_FAILURE(status
)) { return; }
1200 DateTimePatternGenerator::setDateTimeFormat(const UnicodeString
& dtFormat
) {
1201 dateTimeFormat
= dtFormat
;
1202 // NUL-terminate for the C API.
1203 dateTimeFormat
.getTerminatedBuffer();
1206 const UnicodeString
&
1207 DateTimePatternGenerator::getDateTimeFormat() const {
1208 return dateTimeFormat
;
1212 DateTimePatternGenerator::setDateTimeFromCalendar(const Locale
& locale
, UErrorCode
& status
) {
1213 const UChar
*resStr
;
1214 int32_t resStrLen
= 0;
1216 Calendar
* fCalendar
= Calendar::createInstance(locale
, status
);
1217 if (U_FAILURE(status
)) { return; }
1219 LocalUResourceBundlePointer
calData(ures_open(NULL
, locale
.getBaseName(), &status
));
1220 ures_getByKey(calData
.getAlias(), DT_DateTimeCalendarTag
, calData
.getAlias(), &status
);
1222 LocalUResourceBundlePointer dateTimePatterns
;
1223 if (fCalendar
!= NULL
&& fCalendar
->getType() != NULL
&& *fCalendar
->getType() != '\0'
1224 && uprv_strcmp(fCalendar
->getType(), DT_DateTimeGregorianTag
) != 0) {
1225 dateTimePatterns
.adoptInstead(ures_getByKeyWithFallback(calData
.getAlias(), fCalendar
->getType(),
1227 ures_getByKeyWithFallback(dateTimePatterns
.getAlias(), DT_DateTimePatternsTag
,
1228 dateTimePatterns
.getAlias(), &status
);
1231 if (dateTimePatterns
.isNull() || status
== U_MISSING_RESOURCE_ERROR
) {
1232 status
= U_ZERO_ERROR
;
1233 dateTimePatterns
.adoptInstead(ures_getByKeyWithFallback(calData
.getAlias(), DT_DateTimeGregorianTag
,
1234 dateTimePatterns
.orphan(), &status
));
1235 ures_getByKeyWithFallback(dateTimePatterns
.getAlias(), DT_DateTimePatternsTag
,
1236 dateTimePatterns
.getAlias(), &status
);
1238 if (U_FAILURE(status
)) { return; }
1240 if (ures_getSize(dateTimePatterns
.getAlias()) <= DateFormat::kDateTime
)
1242 status
= U_INVALID_FORMAT_ERROR
;
1245 resStr
= ures_getStringByIndex(dateTimePatterns
.getAlias(), (int32_t)DateFormat::kDateTime
, &resStrLen
, &status
);
1246 setDateTimeFormat(UnicodeString(TRUE
, resStr
, resStrLen
));
1252 DateTimePatternGenerator::setDecimalSymbols(const Locale
& locale
, UErrorCode
& status
) {
1253 DecimalFormatSymbols dfs
= DecimalFormatSymbols(locale
, status
);
1254 if(U_SUCCESS(status
)) {
1255 decimal
= dfs
.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol
);
1256 // NUL-terminate for the C API.
1257 decimal
.getTerminatedBuffer();
1261 UDateTimePatternConflict
1262 DateTimePatternGenerator::addPattern(
1263 const UnicodeString
& pattern
,
1265 UnicodeString
&conflictingPattern
,
1268 return addPatternWithSkeleton(pattern
, NULL
, override
, conflictingPattern
, status
);
1271 // For DateTimePatternGenerator::addPatternWithSkeleton -
1272 // If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
1273 // 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern.
1274 // 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
1275 // (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
1276 // parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
1277 // specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
1278 // derived (i.e. entries derived from the standard date/time patters for the specified locale).
1279 // 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a
1280 // specified skeleton (which sets a new field in the PtnElem in the PatternMap).
1281 UDateTimePatternConflict
1282 DateTimePatternGenerator::addPatternWithSkeleton(
1283 const UnicodeString
& pattern
,
1284 const UnicodeString
* skeletonToUse
,
1286 UnicodeString
& conflictingPattern
,
1290 UnicodeString basePattern
;
1291 PtnSkeleton skeleton
;
1292 UDateTimePatternConflict conflictingStatus
= UDATPG_NO_CONFLICT
;
1294 DateTimeMatcher matcher
;
1295 if ( skeletonToUse
== NULL
) {
1296 matcher
.set(pattern
, fp
, skeleton
);
1297 matcher
.getBasePattern(basePattern
);
1299 matcher
.set(*skeletonToUse
, fp
, skeleton
); // no longer trims skeleton fields to max len 3, per #7930
1300 matcher
.getBasePattern(basePattern
); // or perhaps instead: basePattern = *skeletonToUse;
1302 // We only care about base conflicts - and replacing the pattern associated with a base - if:
1303 // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
1304 // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
1305 // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
1306 // if we are getting here from a subsequent call to addPattern).
1307 // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
1308 // availableFormats items from root, which should not override any previous entry with the same base.
1309 UBool entryHadSpecifiedSkeleton
;
1310 const UnicodeString
*duplicatePattern
= patternMap
->getPatternFromBasePattern(basePattern
, entryHadSpecifiedSkeleton
);
1311 if (duplicatePattern
!= NULL
&& (!entryHadSpecifiedSkeleton
|| (skeletonToUse
!= NULL
&& !override
))) {
1312 conflictingStatus
= UDATPG_BASE_CONFLICT
;
1313 conflictingPattern
= *duplicatePattern
;
1315 return conflictingStatus
;
1318 // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
1319 // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
1320 // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
1321 // the previously-specified conflicting item.
1322 const PtnSkeleton
* entrySpecifiedSkeleton
= NULL
;
1323 duplicatePattern
= patternMap
->getPatternFromSkeleton(skeleton
, &entrySpecifiedSkeleton
);
1324 if (duplicatePattern
!= NULL
) {
1325 conflictingStatus
= UDATPG_CONFLICT
;
1326 conflictingPattern
= *duplicatePattern
;
1327 if (!override
|| (skeletonToUse
!= NULL
&& entrySpecifiedSkeleton
!= NULL
)) {
1328 return conflictingStatus
;
1331 patternMap
->add(basePattern
, skeleton
, pattern
, skeletonToUse
!= NULL
, status
);
1332 if(U_FAILURE(status
)) {
1333 return conflictingStatus
;
1336 return UDATPG_NO_CONFLICT
;
1340 UDateTimePatternField
1341 DateTimePatternGenerator::getAppendFormatNumber(const char* field
) const {
1342 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1343 if (uprv_strcmp(CLDR_FIELD_APPEND
[i
], field
)==0) {
1344 return (UDateTimePatternField
)i
;
1347 return UDATPG_FIELD_COUNT
;
1350 UDateTimePatternField
1351 DateTimePatternGenerator::getFieldAndWidthIndices(const char* key
, UDateTimePGDisplayWidth
* widthP
) const {
1352 char cldrFieldKey
[UDATPG_FIELD_KEY_MAX
+ 1];
1353 uprv_strncpy(cldrFieldKey
, key
, UDATPG_FIELD_KEY_MAX
);
1354 cldrFieldKey
[UDATPG_FIELD_KEY_MAX
]=0; // ensure termination
1355 *widthP
= UDATPG_WIDE
;
1356 char* hyphenPtr
= uprv_strchr(cldrFieldKey
, '-');
1358 for (int32_t i
=UDATPG_WIDTH_COUNT
-1; i
>0; --i
) {
1359 if (uprv_strcmp(CLDR_FIELD_WIDTH
[i
], hyphenPtr
)==0) {
1360 *widthP
=(UDateTimePGDisplayWidth
)i
;
1364 *hyphenPtr
= 0; // now delete width portion of key
1366 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1367 if (uprv_strcmp(CLDR_FIELD_NAME
[i
],cldrFieldKey
)==0) {
1368 return (UDateTimePatternField
)i
;
1371 return UDATPG_FIELD_COUNT
;
1374 const UnicodeString
*
1375 DateTimePatternGenerator::getBestRaw(DateTimeMatcher
& source
,
1376 int32_t includeMask
,
1377 DistanceInfo
* missingFields
,
1378 const PtnSkeleton
** specifiedSkeletonPtr
) {
1379 int32_t bestDistance
= 0x7fffffff;
1380 DistanceInfo tempInfo
;
1381 const UnicodeString
*bestPattern
=NULL
;
1382 const PtnSkeleton
* specifiedSkeleton
=NULL
;
1384 PatternMapIterator it
;
1385 for (it
.set(*patternMap
); it
.hasNext(); ) {
1386 DateTimeMatcher trial
= it
.next();
1387 if (trial
.equals(skipMatcher
)) {
1390 int32_t distance
=source
.getDistance(trial
, includeMask
, tempInfo
);
1391 if (distance
<bestDistance
) {
1392 bestDistance
=distance
;
1393 bestPattern
=patternMap
->getPatternFromSkeleton(*trial
.getSkeletonPtr(), &specifiedSkeleton
);
1394 missingFields
->setTo(tempInfo
);
1401 // If the best raw match had a specified skeleton and that skeleton was requested by the caller,
1402 // then return it too. This generally happens when the caller needs to pass that skeleton
1403 // through to adjustFieldTypes so the latter can do a better job.
1404 if (bestPattern
&& specifiedSkeletonPtr
) {
1405 *specifiedSkeletonPtr
= specifiedSkeleton
;
1411 DateTimePatternGenerator::adjustFieldTypes(const UnicodeString
& pattern
,
1412 const PtnSkeleton
* specifiedSkeleton
,
1414 UDateTimePatternMatchOptions options
) {
1415 UnicodeString newPattern
;
1417 for (int32_t i
=0; i
< fp
->itemNumber
; i
++) {
1418 UnicodeString field
= fp
->items
[i
];
1419 if ( fp
->isQuoteLiteral(field
) ) {
1421 UnicodeString quoteLiteral
;
1422 fp
->getQuoteLiteral(quoteLiteral
, &i
);
1423 newPattern
+= quoteLiteral
;
1426 if (fp
->isPatternSeparator(field
)) {
1430 int32_t canonicalIndex
= fp
->getCanonicalIndex(field
);
1431 if (canonicalIndex
< 0) {
1433 continue; // don't adjust
1435 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
1436 int32_t typeValue
= row
->field
;
1438 // handle day periods - with #13183, no longer need special handling here, integrated with normal types
1440 if ((flags
& kDTPGFixFractionalSeconds
) != 0 && typeValue
== UDATPG_SECOND_FIELD
) {
1442 dtMatcher
->skeleton
.original
.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD
, field
);
1443 } else if (dtMatcher
->skeleton
.type
[typeValue
]!=0) {
1445 // - "reqField" is the field from the originally requested skeleton, with length
1447 // - "field" is the field from the found pattern.
1449 // The adjusted field should consist of characters from the originally requested
1450 // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
1451 // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
1452 // of characters from the found pattern.
1454 // The length of the adjusted field (adjFieldLen) should match that in the originally
1455 // requested skeleton, except that in the following cases the length of the adjusted field
1456 // should match that in the found pattern (i.e. the length of this pattern field should
1457 // not be adjusted):
1458 // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is
1459 // not set (ticket #7180). Note, we may want to implement a similar change for other
1460 // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for
1461 // field length, but options bits can be used to override this.
1462 // 2. There is a specified skeleton for the found pattern and one of the following is true:
1463 // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
1464 // b) The pattern field is numeric and the skeleton field is not, or vice versa.
1466 UChar reqFieldChar
= dtMatcher
->skeleton
.original
.getFieldChar(typeValue
);
1467 int32_t reqFieldLen
= dtMatcher
->skeleton
.original
.getFieldLength(typeValue
);
1468 if (reqFieldChar
== CAP_E
&& reqFieldLen
< 3)
1469 reqFieldLen
= 3; // 1-3 for E are equivalent to 3 for c,e
1470 int32_t adjFieldLen
= reqFieldLen
;
1471 if ( (typeValue
==UDATPG_HOUR_FIELD
&& (options
& UDATPG_MATCH_HOUR_FIELD_LENGTH
)==0) ||
1472 (typeValue
==UDATPG_MINUTE_FIELD
&& (options
& UDATPG_MATCH_MINUTE_FIELD_LENGTH
)==0) ||
1473 (typeValue
==UDATPG_SECOND_FIELD
&& (options
& UDATPG_MATCH_SECOND_FIELD_LENGTH
)==0) ) {
1474 adjFieldLen
= field
.length();
1475 } else if (specifiedSkeleton
) {
1476 int32_t skelFieldLen
= specifiedSkeleton
->original
.getFieldLength(typeValue
);
1477 UBool patFieldIsNumeric
= (row
->type
> 0);
1478 UBool skelFieldIsNumeric
= (specifiedSkeleton
->type
[typeValue
] > 0);
1479 if (skelFieldLen
== reqFieldLen
|| (patFieldIsNumeric
&& !skelFieldIsNumeric
) || (skelFieldIsNumeric
&& !patFieldIsNumeric
)) {
1480 // don't adjust the field length in the found pattern
1481 adjFieldLen
= field
.length();
1484 UChar c
= (typeValue
!= UDATPG_HOUR_FIELD
1485 && typeValue
!= UDATPG_MONTH_FIELD
1486 && typeValue
!= UDATPG_WEEKDAY_FIELD
1487 && (typeValue
!= UDATPG_YEAR_FIELD
|| reqFieldChar
==CAP_Y
))
1490 if (typeValue
== UDATPG_HOUR_FIELD
&& (flags
& kDTPGSkeletonUsesCapJ
) != 0) {
1491 c
= fDefaultHourFormatChar
;
1492 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1493 case UADATPG_FORCE_12_HOUR_CYCLE
:
1494 if (c
== CAP_H
|| c
== LOW_K
) {
1495 // Have 24-hour cycle, change to 12-hour cycle.
1496 // Should have better way to pick 'h' or 'K'.
1500 case UADATPG_FORCE_24_HOUR_CYCLE
:
1501 if (c
== LOW_H
|| c
== CAP_K
) {
1502 // Have 12-hour cycle, change to 24-hour cycle.
1503 // Should have better way to pick 'H' or 'k'.
1512 for (int32_t i
=adjFieldLen
; i
>0; --i
) {
1523 DateTimePatternGenerator::getBestAppending(int32_t missingFields
, int32_t flags
, UDateTimePatternMatchOptions options
) {
1524 UnicodeString resultPattern
, tempPattern
;
1525 UErrorCode err
=U_ZERO_ERROR
;
1526 int32_t lastMissingFieldMask
=0;
1527 if (missingFields
!=0) {
1528 resultPattern
=UnicodeString();
1529 const PtnSkeleton
* specifiedSkeleton
=NULL
;
1530 tempPattern
= *getBestRaw(*dtMatcher
, missingFields
, distanceInfo
, &specifiedSkeleton
);
1531 resultPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1532 if ( distanceInfo
->missingFieldMask
==0 ) {
1533 return resultPattern
;
1535 while (distanceInfo
->missingFieldMask
!=0) { // precondition: EVERY single field must work!
1536 if ( lastMissingFieldMask
== distanceInfo
->missingFieldMask
) {
1537 break; // cannot find the proper missing field
1539 if (((distanceInfo
->missingFieldMask
& UDATPG_SECOND_AND_FRACTIONAL_MASK
)==UDATPG_FRACTIONAL_MASK
) &&
1540 ((missingFields
& UDATPG_SECOND_AND_FRACTIONAL_MASK
) == UDATPG_SECOND_AND_FRACTIONAL_MASK
)) {
1541 resultPattern
= adjustFieldTypes(resultPattern
, specifiedSkeleton
, flags
| kDTPGFixFractionalSeconds
, options
);
1542 distanceInfo
->missingFieldMask
&= ~UDATPG_FRACTIONAL_MASK
;
1545 int32_t startingMask
= distanceInfo
->missingFieldMask
;
1546 tempPattern
= *getBestRaw(*dtMatcher
, distanceInfo
->missingFieldMask
, distanceInfo
, &specifiedSkeleton
);
1547 tempPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1548 int32_t foundMask
=startingMask
& ~distanceInfo
->missingFieldMask
;
1549 int32_t topField
=getTopBitNumber(foundMask
);
1550 UnicodeString appendName
;
1551 getAppendName((UDateTimePatternField
)topField
, appendName
);
1552 const UnicodeString
*values
[3] = {
1557 SimpleFormatter(appendItemFormats
[topField
], 2, 3, err
).
1558 formatAndReplace(values
, 3, resultPattern
, NULL
, 0, err
);
1559 lastMissingFieldMask
= distanceInfo
->missingFieldMask
;
1562 return resultPattern
;
1566 DateTimePatternGenerator::getTopBitNumber(int32_t foundMask
) {
1567 if ( foundMask
==0 ) {
1571 while (foundMask
!=0) {
1575 if (i
-1 >UDATPG_ZONE_FIELD
) {
1576 return UDATPG_ZONE_FIELD
;
1583 DateTimePatternGenerator::setAvailableFormat(const UnicodeString
&key
, UErrorCode
& err
)
1585 fAvailableFormatKeyHash
->puti(key
, 1, err
);
1589 DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString
&key
) const {
1590 return (UBool
)(fAvailableFormatKeyHash
->geti(key
) == 1);
1594 DateTimePatternGenerator::copyHashtable(Hashtable
*other
, UErrorCode
&status
) {
1596 if (other
== NULL
) {
1599 if (fAvailableFormatKeyHash
!= NULL
) {
1600 delete fAvailableFormatKeyHash
;
1601 fAvailableFormatKeyHash
= NULL
;
1603 initHashtable(status
);
1604 if(U_FAILURE(status
)){
1607 int32_t pos
= UHASH_FIRST
;
1608 const UHashElement
* elem
= NULL
;
1609 // walk through the hash table and create a deep clone
1610 while((elem
= other
->nextElement(pos
))!= NULL
){
1611 const UHashTok otherKeyTok
= elem
->key
;
1612 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
1613 fAvailableFormatKeyHash
->puti(*otherKey
, 1, status
);
1614 if(U_FAILURE(status
)){
1621 DateTimePatternGenerator::getSkeletons(UErrorCode
& status
) const {
1622 StringEnumeration
* skeletonEnumerator
= new DTSkeletonEnumeration(*patternMap
, DT_SKELETON
, status
);
1623 return skeletonEnumerator
;
1626 const UnicodeString
&
1627 DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString
& skeleton
) const {
1630 if (skeleton
.length() ==0) {
1633 curElem
= patternMap
->getHeader(skeleton
.charAt(0));
1634 while ( curElem
!= NULL
) {
1635 if ( curElem
->skeleton
->getSkeleton()==skeleton
) {
1636 return curElem
->pattern
;
1638 curElem
=curElem
->next
;
1644 DateTimePatternGenerator::getBaseSkeletons(UErrorCode
& status
) const {
1645 StringEnumeration
* baseSkeletonEnumerator
= new DTSkeletonEnumeration(*patternMap
, DT_BASESKELETON
, status
);
1646 return baseSkeletonEnumerator
;
1650 DateTimePatternGenerator::getRedundants(UErrorCode
& status
) {
1651 StringEnumeration
* output
= new DTRedundantEnumeration();
1652 const UnicodeString
*pattern
;
1653 PatternMapIterator it
;
1654 for (it
.set(*patternMap
); it
.hasNext(); ) {
1655 DateTimeMatcher current
= it
.next();
1656 pattern
= patternMap
->getPatternFromSkeleton(*(it
.getSkeleton()));
1657 if ( isCanonicalItem(*pattern
) ) {
1660 if ( skipMatcher
== NULL
) {
1661 skipMatcher
= new DateTimeMatcher(current
);
1664 *skipMatcher
= current
;
1666 UnicodeString trial
= getBestPattern(current
.getPattern(), status
);
1667 if (trial
== *pattern
) {
1668 ((DTRedundantEnumeration
*)output
)->add(*pattern
, status
);
1670 if (current
.equals(skipMatcher
)) {
1678 DateTimePatternGenerator::isCanonicalItem(const UnicodeString
& item
) const {
1679 if ( item
.length() != 1 ) {
1682 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1683 if (item
.charAt(0)==Canonical_Items
[i
]) {
1691 DateTimePatternGenerator
*
1692 DateTimePatternGenerator::clone() const {
1693 return new DateTimePatternGenerator(*this);
1696 PatternMap::PatternMap() {
1697 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1700 isDupAllowed
= TRUE
;
1704 PatternMap::copyFrom(const PatternMap
& other
, UErrorCode
& status
) {
1705 this->isDupAllowed
= other
.isDupAllowed
;
1706 for (int32_t bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
1707 PtnElem
*curElem
, *otherElem
, *prevElem
=NULL
;
1708 otherElem
= other
.boot
[bootIndex
];
1709 while (otherElem
!=NULL
) {
1710 if ((curElem
= new PtnElem(otherElem
->basePattern
, otherElem
->pattern
))==NULL
) {
1712 status
= U_MEMORY_ALLOCATION_ERROR
;
1715 if ( this->boot
[bootIndex
]== NULL
) {
1716 this->boot
[bootIndex
] = curElem
;
1718 if ((curElem
->skeleton
=new PtnSkeleton(*(otherElem
->skeleton
))) == NULL
) {
1720 status
= U_MEMORY_ALLOCATION_ERROR
;
1723 curElem
->skeletonWasSpecified
= otherElem
->skeletonWasSpecified
;
1724 if (prevElem
!=NULL
) {
1725 prevElem
->next
=curElem
;
1729 otherElem
= otherElem
->next
;
1736 PatternMap::getHeader(UChar baseChar
) {
1739 if ( (baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
) ) {
1740 curElem
= boot
[baseChar
-CAP_A
];
1743 if ( (baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
) ) {
1744 curElem
= boot
[26+baseChar
-LOW_A
];
1753 PatternMap::~PatternMap() {
1754 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1755 if (boot
[i
]!=NULL
) {
1760 } // PatternMap destructor
1763 PatternMap::add(const UnicodeString
& basePattern
,
1764 const PtnSkeleton
& skeleton
,
1765 const UnicodeString
& value
,// mapped pattern value
1766 UBool skeletonWasSpecified
,
1767 UErrorCode
&status
) {
1768 UChar baseChar
= basePattern
.charAt(0);
1769 PtnElem
*curElem
, *baseElem
;
1770 status
= U_ZERO_ERROR
;
1772 // the baseChar must be A-Z or a-z
1773 if ((baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
)) {
1774 baseElem
= boot
[baseChar
-CAP_A
];
1777 if ((baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
)) {
1778 baseElem
= boot
[26+baseChar
-LOW_A
];
1781 status
= U_ILLEGAL_CHARACTER
;
1786 if (baseElem
== NULL
) {
1787 if ((curElem
= new PtnElem(basePattern
, value
)) == NULL
) {
1789 status
= U_MEMORY_ALLOCATION_ERROR
;
1792 if (baseChar
>= LOW_A
) {
1793 boot
[26 + (baseChar
-LOW_A
)] = curElem
;
1796 boot
[baseChar
-CAP_A
] = curElem
;
1798 curElem
->skeleton
= new PtnSkeleton(skeleton
);
1799 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1801 if ( baseElem
!= NULL
) {
1802 curElem
= getDuplicateElem(basePattern
, skeleton
, baseElem
);
1804 if (curElem
== NULL
) {
1805 // add new element to the list.
1807 while( curElem
-> next
!= NULL
)
1809 curElem
= curElem
->next
;
1811 if ((curElem
->next
= new PtnElem(basePattern
, value
)) == NULL
) {
1813 status
= U_MEMORY_ALLOCATION_ERROR
;
1816 curElem
=curElem
->next
;
1817 curElem
->skeleton
= new PtnSkeleton(skeleton
);
1818 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1821 // Pattern exists in the list already.
1822 if ( !isDupAllowed
) {
1825 // Overwrite the value.
1826 curElem
->pattern
= value
;
1827 // It was a bug that we were not doing the following previously,
1828 // though that bug hid other problems by making things partly work.
1829 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1832 } // PatternMap::add
1834 // Find the pattern from the given basePattern string.
1835 const UnicodeString
*
1836 PatternMap::getPatternFromBasePattern(UnicodeString
& basePattern
, UBool
& skeletonWasSpecified
) { // key to search for
1839 if ((curElem
=getHeader(basePattern
.charAt(0)))==NULL
) {
1840 return NULL
; // no match
1844 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
1845 skeletonWasSpecified
= curElem
->skeletonWasSpecified
;
1846 return &(curElem
->pattern
);
1848 curElem
=curElem
->next
;
1849 }while (curElem
!= NULL
);
1852 } // PatternMap::getFromBasePattern
1855 // Find the pattern from the given skeleton.
1856 // At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL),
1857 // the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw)
1858 // and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the
1859 // optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL),
1860 // for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily.
1861 const UnicodeString
*
1862 PatternMap::getPatternFromSkeleton(PtnSkeleton
& skeleton
, const PtnSkeleton
** specifiedSkeletonPtr
) { // key to search for
1865 if (specifiedSkeletonPtr
) {
1866 *specifiedSkeletonPtr
= NULL
;
1870 UChar baseChar
= skeleton
.getFirstChar();
1871 if ((curElem
=getHeader(baseChar
))==NULL
) {
1872 return NULL
; // no match
1877 if (specifiedSkeletonPtr
!= NULL
) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
1878 equal
= curElem
->skeleton
->original
== skeleton
.original
;
1879 } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
1880 equal
= curElem
->skeleton
->baseOriginal
== skeleton
.baseOriginal
;
1883 if (specifiedSkeletonPtr
&& curElem
->skeletonWasSpecified
) {
1884 *specifiedSkeletonPtr
= curElem
->skeleton
;
1886 return &(curElem
->pattern
);
1888 curElem
=curElem
->next
;
1889 }while (curElem
!= NULL
);
1895 PatternMap::equals(const PatternMap
& other
) {
1896 if ( this==&other
) {
1899 for (int32_t bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
1900 if ( boot
[bootIndex
]==other
.boot
[bootIndex
] ) {
1903 if ( (boot
[bootIndex
]==NULL
)||(other
.boot
[bootIndex
]==NULL
) ) {
1906 PtnElem
*otherElem
= other
.boot
[bootIndex
];
1907 PtnElem
*myElem
= boot
[bootIndex
];
1908 while ((otherElem
!=NULL
) || (myElem
!=NULL
)) {
1909 if ( myElem
== otherElem
) {
1912 if ((otherElem
==NULL
) || (myElem
==NULL
)) {
1915 if ( (myElem
->basePattern
!= otherElem
->basePattern
) ||
1916 (myElem
->pattern
!= otherElem
->pattern
) ) {
1919 if ((myElem
->skeleton
!=otherElem
->skeleton
)&&
1920 !myElem
->skeleton
->equals(*(otherElem
->skeleton
))) {
1923 myElem
= myElem
->next
;
1924 otherElem
=otherElem
->next
;
1930 // find any key existing in the mapping table already.
1931 // return TRUE if there is an existing key, otherwise return FALSE.
1933 PatternMap::getDuplicateElem(
1934 const UnicodeString
&basePattern
,
1935 const PtnSkeleton
&skeleton
,
1936 PtnElem
*baseElem
) {
1939 if ( baseElem
== (PtnElem
*)NULL
) {
1940 return (PtnElem
*)NULL
;
1946 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
1948 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1949 if (curElem
->skeleton
->type
[i
] != skeleton
.type
[i
] ) {
1958 curElem
= curElem
->next
;
1959 } while( curElem
!= (PtnElem
*)NULL
);
1962 return (PtnElem
*)NULL
;
1964 } // PatternMap::getDuplicateElem
1966 DateTimeMatcher::DateTimeMatcher(void) {
1969 DateTimeMatcher::~DateTimeMatcher() {}
1971 DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher
& other
) {
1972 copyFrom(other
.skeleton
);
1977 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
) {
1978 PtnSkeleton localSkeleton
;
1979 return set(pattern
, fp
, localSkeleton
);
1983 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
, PtnSkeleton
& skeletonResult
) {
1985 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1986 skeletonResult
.type
[i
] = NONE
;
1988 skeletonResult
.original
.clear();
1989 skeletonResult
.baseOriginal
.clear();
1990 skeletonResult
.addedDefaultDayPeriod
= FALSE
;
1993 for (i
=0; i
< fp
->itemNumber
; i
++) {
1994 const UnicodeString
& value
= fp
->items
[i
];
1995 // don't skip 'a' anymore, dayPeriod handled specially below
1997 if ( fp
->isQuoteLiteral(value
) ) {
1998 UnicodeString quoteLiteral
;
1999 fp
->getQuoteLiteral(quoteLiteral
, &i
);
2002 int32_t canonicalIndex
= fp
->getCanonicalIndex(value
);
2003 if (canonicalIndex
< 0 ) {
2006 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
2007 int32_t field
= row
->field
;
2008 skeletonResult
.original
.populate(field
, value
);
2009 UChar repeatChar
= row
->patternChar
;
2010 int32_t repeatCount
= row
->minLen
;
2011 skeletonResult
.baseOriginal
.populate(field
, repeatChar
, repeatCount
);
2012 int16_t subField
= row
->type
;
2013 if ( row
->type
> 0) {
2014 subField
+= value
.length();
2016 skeletonResult
.type
[field
] = subField
;
2018 // #13183, handle special behavior for day period characters (a, b, B)
2019 if (!skeletonResult
.original
.isFieldEmpty(UDATPG_HOUR_FIELD
)) {
2020 if (skeletonResult
.original
.getFieldChar(UDATPG_HOUR_FIELD
)==LOW_H
|| skeletonResult
.original
.getFieldChar(UDATPG_HOUR_FIELD
)==CAP_K
) {
2021 // We have a skeleton with 12-hour-cycle format
2022 if (skeletonResult
.original
.isFieldEmpty(UDATPG_DAYPERIOD_FIELD
)) {
2023 // But we do not have a day period in the skeleton; add the default DAYPERIOD (currently "a")
2024 for (i
= 0; dtTypes
[i
].patternChar
!= 0; i
++) {
2025 if ( dtTypes
[i
].field
== UDATPG_DAYPERIOD_FIELD
) {
2026 // first entry for UDATPG_DAYPERIOD_FIELD
2027 skeletonResult
.original
.populate(UDATPG_DAYPERIOD_FIELD
, dtTypes
[i
].patternChar
, dtTypes
[i
].minLen
);
2028 skeletonResult
.baseOriginal
.populate(UDATPG_DAYPERIOD_FIELD
, dtTypes
[i
].patternChar
, dtTypes
[i
].minLen
);
2029 skeletonResult
.type
[UDATPG_DAYPERIOD_FIELD
] = dtTypes
[i
].type
;
2030 skeletonResult
.addedDefaultDayPeriod
= TRUE
;
2036 // Skeleton has 24-hour-cycle hour format and has dayPeriod, delete dayPeriod (i.e. ignore it)
2037 skeletonResult
.original
.clearField(UDATPG_DAYPERIOD_FIELD
);
2038 skeletonResult
.baseOriginal
.clearField(UDATPG_DAYPERIOD_FIELD
);
2039 skeletonResult
.type
[UDATPG_DAYPERIOD_FIELD
] = NONE
;
2042 copyFrom(skeletonResult
);
2046 DateTimeMatcher::getBasePattern(UnicodeString
&result
) {
2047 result
.remove(); // Reset the result first.
2048 skeleton
.baseOriginal
.appendTo(result
);
2052 DateTimeMatcher::getPattern() {
2053 UnicodeString result
;
2054 return skeleton
.original
.appendTo(result
);
2058 DateTimeMatcher::getDistance(const DateTimeMatcher
& other
, int32_t includeMask
, DistanceInfo
& distanceInfo
) {
2060 distanceInfo
.clear();
2061 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2062 int32_t myType
= (includeMask
&(1<<i
))==0 ? 0 : skeleton
.type
[i
];
2063 int32_t otherType
= other
.skeleton
.type
[i
];
2064 if (myType
==otherType
) {
2067 if (myType
==0) {// and other is not
2068 result
+= EXTRA_FIELD
;
2069 distanceInfo
.addExtra(i
);
2073 result
+= MISSING_FIELD
;
2074 distanceInfo
.addMissing(i
);
2077 result
+= abs(myType
- otherType
);
2086 DateTimeMatcher::copyFrom(const PtnSkeleton
& newSkeleton
) {
2087 skeleton
.copyFrom(newSkeleton
);
2091 DateTimeMatcher::copyFrom() {
2097 DateTimeMatcher::equals(const DateTimeMatcher
* other
) const {
2098 if (other
==NULL
) { return FALSE
; }
2099 return skeleton
.original
== other
->skeleton
.original
;
2103 DateTimeMatcher::getFieldMask() {
2106 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2107 if (skeleton
.type
[i
]!=0) {
2115 DateTimeMatcher::getSkeletonPtr() {
2119 FormatParser::FormatParser () {
2125 FormatParser::~FormatParser () {
2129 // Find the next token with the starting position and length
2130 // Note: the startPos may
2131 FormatParser::TokenStatus
2132 FormatParser::setTokens(const UnicodeString
& pattern
, int32_t startPos
, int32_t *len
) {
2133 int32_t curLoc
= startPos
;
2134 if ( curLoc
>= pattern
.length()) {
2137 // check the current char is between A-Z or a-z
2139 UChar c
=pattern
.charAt(curLoc
);
2140 if ( (c
>=CAP_A
&& c
<=CAP_Z
) || (c
>=LOW_A
&& c
<=LOW_Z
) ) {
2149 if ( pattern
.charAt(curLoc
)!= pattern
.charAt(startPos
) ) {
2150 break; // not the same token
2152 } while(curLoc
<= pattern
.length());
2153 *len
= curLoc
-startPos
;
2158 FormatParser::set(const UnicodeString
& pattern
) {
2160 TokenStatus result
=START
;
2165 result
= setTokens( pattern
, startPos
, &len
);
2166 if ( result
== ADD_TOKEN
)
2168 items
[itemNumber
++] = UnicodeString(pattern
, startPos
, len
);
2174 } while (result
==ADD_TOKEN
&& itemNumber
< MAX_DT_TOKEN
);
2178 FormatParser::getCanonicalIndex(const UnicodeString
& s
, UBool strict
) {
2179 int32_t len
= s
.length();
2183 UChar ch
= s
.charAt(0);
2185 // Verify that all are the same character.
2186 for (int32_t l
= 1; l
< len
; l
++) {
2187 if (ch
!= s
.charAt(l
)) {
2192 int32_t bestRow
= -1;
2193 while (dtTypes
[i
].patternChar
!= 0x0000) {
2194 if ( dtTypes
[i
].patternChar
!= ch
) {
2199 if (dtTypes
[i
].patternChar
!= dtTypes
[i
+1].patternChar
) {
2202 if (dtTypes
[i
+1].minLen
<= len
) {
2208 return strict
? -1 : bestRow
;
2212 FormatParser::isQuoteLiteral(const UnicodeString
& s
) {
2213 return (UBool
)(s
.charAt(0)==SINGLE_QUOTE
);
2216 // This function aussumes the current itemIndex points to the quote literal.
2217 // Please call isQuoteLiteral prior to this function.
2219 FormatParser::getQuoteLiteral(UnicodeString
& quote
, int32_t *itemIndex
) {
2220 int32_t i
=*itemIndex
;
2223 if (items
[i
].charAt(0)==SINGLE_QUOTE
) {
2227 while ( i
< itemNumber
) {
2228 if ( items
[i
].charAt(0)==SINGLE_QUOTE
) {
2229 if ( (i
+1<itemNumber
) && (items
[i
+1].charAt(0)==SINGLE_QUOTE
)) {
2230 // two single quotes e.g. 'o''clock'
2231 quote
+= items
[i
++];
2232 quote
+= items
[i
++];
2249 FormatParser::isPatternSeparator(UnicodeString
& field
) {
2250 for (int32_t i
=0; i
<field
.length(); ++i
) {
2251 UChar c
= field
.charAt(i
);
2252 if ( (c
==SINGLE_QUOTE
) || (c
==BACKSLASH
) || (c
==SPACE
) || (c
==COLON
) ||
2253 (c
==QUOTATION_MARK
) || (c
==COMMA
) || (c
==HYPHEN
) ||(items
[i
].charAt(0)==DOT
) ) {
2263 DistanceInfo::~DistanceInfo() {}
2266 DistanceInfo::setTo(DistanceInfo
&other
) {
2267 missingFieldMask
= other
.missingFieldMask
;
2268 extraFieldMask
= other
.extraFieldMask
;
2271 PatternMapIterator::PatternMapIterator() {
2275 matcher
= new DateTimeMatcher();
2279 PatternMapIterator::~PatternMapIterator() {
2284 PatternMapIterator::set(PatternMap
& newPatternMap
) {
2285 this->patternMap
=&newPatternMap
;
2289 PatternMapIterator::getSkeleton() {
2290 if ( nodePtr
== NULL
) {
2294 return nodePtr
->skeleton
;
2299 PatternMapIterator::hasNext() {
2300 int32_t headIndex
=bootIndex
;
2301 PtnElem
*curPtr
=nodePtr
;
2303 if (patternMap
==NULL
) {
2306 while ( headIndex
< MAX_PATTERN_ENTRIES
) {
2307 if ( curPtr
!= NULL
) {
2308 if ( curPtr
->next
!= NULL
) {
2318 if ( patternMap
->boot
[headIndex
] != NULL
) {
2332 PatternMapIterator::next() {
2333 while ( bootIndex
< MAX_PATTERN_ENTRIES
) {
2334 if ( nodePtr
!= NULL
) {
2335 if ( nodePtr
->next
!= NULL
) {
2336 nodePtr
= nodePtr
->next
;
2346 if ( patternMap
->boot
[bootIndex
] != NULL
) {
2347 nodePtr
= patternMap
->boot
[bootIndex
];
2356 if (nodePtr
!=NULL
) {
2357 matcher
->copyFrom(*nodePtr
->skeleton
);
2360 matcher
->copyFrom();
2366 SkeletonFields::SkeletonFields() {
2367 // Set initial values to zero
2371 void SkeletonFields::clear() {
2372 uprv_memset(chars
, 0, sizeof(chars
));
2373 uprv_memset(lengths
, 0, sizeof(lengths
));
2376 void SkeletonFields::copyFrom(const SkeletonFields
& other
) {
2377 uprv_memcpy(chars
, other
.chars
, sizeof(chars
));
2378 uprv_memcpy(lengths
, other
.lengths
, sizeof(lengths
));
2381 void SkeletonFields::clearField(int32_t field
) {
2386 UChar
SkeletonFields::getFieldChar(int32_t field
) const {
2387 return chars
[field
];
2390 int32_t SkeletonFields::getFieldLength(int32_t field
) const {
2391 return lengths
[field
];
2394 void SkeletonFields::populate(int32_t field
, const UnicodeString
& value
) {
2395 populate(field
, value
.charAt(0), value
.length());
2398 void SkeletonFields::populate(int32_t field
, UChar ch
, int32_t length
) {
2399 chars
[field
] = (int8_t) ch
;
2400 lengths
[field
] = (int8_t) length
;
2403 UBool
SkeletonFields::isFieldEmpty(int32_t field
) const {
2404 return lengths
[field
] == 0;
2407 UnicodeString
& SkeletonFields::appendTo(UnicodeString
& string
) const {
2408 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2409 appendFieldTo(i
, string
);
2414 UnicodeString
& SkeletonFields::appendFieldTo(int32_t field
, UnicodeString
& string
) const {
2415 UChar
ch(chars
[field
]);
2416 int32_t length
= (int32_t) lengths
[field
];
2418 for (int32_t i
=0; i
<length
; i
++) {
2424 UChar
SkeletonFields::getFirstChar() const {
2425 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2426 if (lengths
[i
] != 0) {
2434 PtnSkeleton::PtnSkeleton() {
2437 PtnSkeleton::PtnSkeleton(const PtnSkeleton
& other
) {
2441 void PtnSkeleton::copyFrom(const PtnSkeleton
& other
) {
2442 uprv_memcpy(type
, other
.type
, sizeof(type
));
2443 original
.copyFrom(other
.original
);
2444 baseOriginal
.copyFrom(other
.baseOriginal
);
2447 void PtnSkeleton::clear() {
2448 uprv_memset(type
, 0, sizeof(type
));
2450 baseOriginal
.clear();
2454 PtnSkeleton::equals(const PtnSkeleton
& other
) const {
2455 return (original
== other
.original
)
2456 && (baseOriginal
== other
.baseOriginal
)
2457 && (uprv_memcmp(type
, other
.type
, sizeof(type
)) == 0);
2461 PtnSkeleton::getSkeleton() const {
2462 UnicodeString result
;
2463 result
= original
.appendTo(result
);
2465 if (addedDefaultDayPeriod
&& (pos
= result
.indexOf(LOW_A
)) >= 0) {
2466 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2467 // was not in the provided skeleton, remove it here before returning skeleton.
2468 result
.remove(pos
, 1);
2474 PtnSkeleton::getBaseSkeleton() const {
2475 UnicodeString result
;
2476 result
= baseOriginal
.appendTo(result
);
2478 if (addedDefaultDayPeriod
&& (pos
= result
.indexOf(LOW_A
)) >= 0) {
2479 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2480 // was not in the provided skeleton, remove it here before returning skeleton.
2481 result
.remove(pos
, 1);
2487 PtnSkeleton::getFirstChar() const {
2488 return baseOriginal
.getFirstChar();
2491 PtnSkeleton::~PtnSkeleton() {
2494 PtnElem::PtnElem(const UnicodeString
&basePat
, const UnicodeString
&pat
) :
2495 basePattern(basePat
),
2502 PtnElem::~PtnElem() {
2510 DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap
&patternMap
, dtStrEnum type
, UErrorCode
& status
) {
2512 PtnSkeleton
*curSkeleton
;
2517 fSkeletons
= new UVector(status
);
2518 if (U_FAILURE(status
)) {
2522 for (bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
2523 curElem
= patternMap
.boot
[bootIndex
];
2524 while (curElem
!=NULL
) {
2526 case DT_BASESKELETON
:
2527 s
=curElem
->basePattern
;
2533 curSkeleton
=curElem
->skeleton
;
2534 s
=curSkeleton
->getSkeleton();
2537 if ( !isCanonicalItem(s
) ) {
2538 fSkeletons
->addElement(new UnicodeString(s
), status
);
2539 if (U_FAILURE(status
)) {
2545 curElem
= curElem
->next
;
2548 if ((bootIndex
==MAX_PATTERN_ENTRIES
) && (curElem
!=NULL
) ) {
2549 status
= U_BUFFER_OVERFLOW_ERROR
;
2553 const UnicodeString
*
2554 DTSkeletonEnumeration::snext(UErrorCode
& status
) {
2555 if (U_SUCCESS(status
) && pos
< fSkeletons
->size()) {
2556 return (const UnicodeString
*)fSkeletons
->elementAt(pos
++);
2562 DTSkeletonEnumeration::reset(UErrorCode
& /*status*/) {
2567 DTSkeletonEnumeration::count(UErrorCode
& /*status*/) const {
2568 return (fSkeletons
==NULL
) ? 0 : fSkeletons
->size();
2572 DTSkeletonEnumeration::isCanonicalItem(const UnicodeString
& item
) {
2573 if ( item
.length() != 1 ) {
2576 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2577 if (item
.charAt(0)==Canonical_Items
[i
]) {
2584 DTSkeletonEnumeration::~DTSkeletonEnumeration() {
2586 for (int32_t i
=0; i
<fSkeletons
->size(); ++i
) {
2587 if ((s
=(UnicodeString
*)fSkeletons
->elementAt(i
))!=NULL
) {
2594 DTRedundantEnumeration::DTRedundantEnumeration() {
2600 DTRedundantEnumeration::add(const UnicodeString
& pattern
, UErrorCode
& status
) {
2601 if (U_FAILURE(status
)) return;
2602 if (fPatterns
== NULL
) {
2603 fPatterns
= new UVector(status
);
2604 if (U_FAILURE(status
)) {
2610 fPatterns
->addElement(new UnicodeString(pattern
), status
);
2611 if (U_FAILURE(status
)) {
2618 const UnicodeString
*
2619 DTRedundantEnumeration::snext(UErrorCode
& status
) {
2620 if (U_SUCCESS(status
) && pos
< fPatterns
->size()) {
2621 return (const UnicodeString
*)fPatterns
->elementAt(pos
++);
2627 DTRedundantEnumeration::reset(UErrorCode
& /*status*/) {
2632 DTRedundantEnumeration::count(UErrorCode
& /*status*/) const {
2633 return (fPatterns
==NULL
) ? 0 : fPatterns
->size();
2637 DTRedundantEnumeration::isCanonicalItem(const UnicodeString
& item
) {
2638 if ( item
.length() != 1 ) {
2641 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2642 if (item
.charAt(0)==Canonical_Items
[i
]) {
2649 DTRedundantEnumeration::~DTRedundantEnumeration() {
2651 for (int32_t i
=0; i
<fPatterns
->size(); ++i
) {
2652 if ((s
=(UnicodeString
*)fPatterns
->elementAt(i
))!=NULL
) {
2662 #endif /* #if !UCONFIG_NO_FORMATTING */