]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/dtptngen.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / dtptngen.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 2007-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 *
9 * File DTPTNGEN.CPP
10 *
11 *******************************************************************************
12 */
13
14 #include "unicode/utypes.h"
15 #if !UCONFIG_NO_FORMATTING
16
17 #include "unicode/datefmt.h"
18 #include "unicode/decimfmt.h"
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/dtptngen.h"
21 #include "unicode/localpointer.h"
22 #include "unicode/schriter.h"
23 #include "unicode/simpleformatter.h"
24 #include "unicode/smpdtfmt.h"
25 #include "unicode/udat.h"
26 #include "unicode/udatpg.h"
27 #include "unicode/uniset.h"
28 #include "unicode/uloc.h"
29 #include "unicode/ures.h"
30 #include "unicode/ustring.h"
31 #include "unicode/rep.h"
32 #include "unicode/region.h"
33 #include "cpputils.h"
34 #include "mutex.h"
35 #include "umutex.h"
36 #include "cmemory.h"
37 #include "cstring.h"
38 #include "locbased.h"
39 #include "hash.h"
40 #include "uhash.h"
41 #include "uresimp.h"
42 #include "dtptngen_impl.h"
43 #include "ucln_in.h"
44 #include "charstr.h"
45 #include "uassert.h"
46
47 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
48 /**
49 * If we are on EBCDIC, use an iterator which will
50 * traverse the bundles in ASCII order.
51 */
52 #define U_USE_ASCII_BUNDLE_ITERATOR
53 #define U_SORT_ASCII_BUNDLE_ITERATOR
54 #endif
55
56 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
57
58 #include "unicode/ustring.h"
59 #include "uarrsort.h"
60
61 struct UResAEntry {
62 UChar *key;
63 UResourceBundle *item;
64 };
65
66 struct UResourceBundleAIterator {
67 UResourceBundle *bund;
68 UResAEntry *entries;
69 int32_t num;
70 int32_t cursor;
71 };
72
73 /* Must be C linkage to pass function pointer to the sort function */
74
75 U_CDECL_BEGIN
76
77 static int32_t U_CALLCONV
78 ures_a_codepointSort(const void *context, const void *left, const void *right) {
79 //CompareContext *cmp=(CompareContext *)context;
80 return u_strcmp(((const UResAEntry *)left)->key,
81 ((const UResAEntry *)right)->key);
82 }
83
84 U_CDECL_END
85
86 static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) {
87 if(U_FAILURE(*status)) {
88 return;
89 }
90 aiter->bund = bund;
91 aiter->num = ures_getSize(aiter->bund);
92 aiter->cursor = 0;
93 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
94 aiter->entries = nullptr;
95 #else
96 aiter->entries = (UResAEntry*)uprv_malloc(sizeof(UResAEntry)*aiter->num);
97 for(int i=0;i<aiter->num;i++) {
98 aiter->entries[i].item = ures_getByIndex(aiter->bund, i, nullptr, status);
99 const char *akey = ures_getKey(aiter->entries[i].item);
100 int32_t len = uprv_strlen(akey)+1;
101 aiter->entries[i].key = (UChar*)uprv_malloc(len*sizeof(UChar));
102 u_charsToUChars(akey, aiter->entries[i].key, len);
103 }
104 uprv_sortArray(aiter->entries, aiter->num, sizeof(UResAEntry), ures_a_codepointSort, nullptr, TRUE, status);
105 #endif
106 }
107
108 static void ures_a_close(UResourceBundleAIterator *aiter) {
109 #if defined(U_SORT_ASCII_BUNDLE_ITERATOR)
110 for(int i=0;i<aiter->num;i++) {
111 uprv_free(aiter->entries[i].key);
112 ures_close(aiter->entries[i].item);
113 }
114 #endif
115 }
116
117 static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_t *len, const char **key, UErrorCode *err) {
118 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
119 return ures_getNextString(aiter->bund, len, key, err);
120 #else
121 if(U_FAILURE(*err)) return nullptr;
122 UResourceBundle *item = aiter->entries[aiter->cursor].item;
123 const UChar* ret = ures_getString(item, len, err);
124 *key = ures_getKey(item);
125 aiter->cursor++;
126 return ret;
127 #endif
128 }
129
130
131 #endif
132
133
134 U_NAMESPACE_BEGIN
135
136 // *****************************************************************************
137 // class DateTimePatternGenerator
138 // *****************************************************************************
139 static const UChar Canonical_Items[] = {
140 // GyQMwWEDFdaHmsSv
141 CAP_G, LOW_Y, CAP_Q, CAP_M, LOW_W, CAP_W, CAP_E,
142 CAP_D, CAP_F, LOW_D, LOW_A, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
143 CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0
144 };
145
146 static const dtTypeElem dtTypes[] = {
147 // patternChar, field, type, minLen, weight
148 {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,},
149 {CAP_G, UDATPG_ERA_FIELD, DT_LONG, 4, 0},
150 {CAP_G, UDATPG_ERA_FIELD, DT_NARROW, 5, 0},
151
152 {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20},
153 {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20},
154 {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20},
155 {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20},
156 {CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3},
157 {CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0},
158 {CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0},
159
160 {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2},
161 {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0},
162 {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0},
163 {CAP_Q, UDATPG_QUARTER_FIELD, DT_NARROW, 5, 0},
164 {LOW_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC + DT_DELTA, 1, 2},
165 {LOW_Q, UDATPG_QUARTER_FIELD, DT_SHORT - DT_DELTA, 3, 0},
166 {LOW_Q, UDATPG_QUARTER_FIELD, DT_LONG - DT_DELTA, 4, 0},
167 {LOW_Q, UDATPG_QUARTER_FIELD, DT_NARROW - DT_DELTA, 5, 0},
168
169 {CAP_M, UDATPG_MONTH_FIELD, DT_NUMERIC, 1, 2},
170 {CAP_M, UDATPG_MONTH_FIELD, DT_SHORT, 3, 0},
171 {CAP_M, UDATPG_MONTH_FIELD, DT_LONG, 4, 0},
172 {CAP_M, UDATPG_MONTH_FIELD, DT_NARROW, 5, 0},
173 {CAP_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 2},
174 {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0},
175 {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0},
176 {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0},
177 {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1},
178
179 {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2},
180
181 {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC, 1, 0},
182
183 {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3},
184 {CAP_E, UDATPG_WEEKDAY_FIELD, DT_LONG, 4, 0},
185 {CAP_E, UDATPG_WEEKDAY_FIELD, DT_NARROW, 5, 0},
186 {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER, 6, 0},
187 {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 2},
188 {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORT - 2*DT_DELTA, 3, 0},
189 {LOW_C, UDATPG_WEEKDAY_FIELD, DT_LONG - 2*DT_DELTA, 4, 0},
190 {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NARROW - 2*DT_DELTA, 5, 0},
191 {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORTER - 2*DT_DELTA, 6, 0},
192 {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical
193 {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORT - DT_DELTA, 3, 0},
194 {LOW_E, UDATPG_WEEKDAY_FIELD, DT_LONG - DT_DELTA, 4, 0},
195 {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NARROW - DT_DELTA, 5, 0},
196 {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER - DT_DELTA, 6, 0},
197
198 {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2},
199 {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, // really internal use, so we don't care
200
201 {CAP_D, UDATPG_DAY_OF_YEAR_FIELD, DT_NUMERIC, 1, 3},
202
203 {CAP_F, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, DT_NUMERIC, 1, 0},
204
205 {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 3},
206 {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_LONG, 4, 0},
207 {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_NARROW, 5, 0},
208 {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - DT_DELTA, 1, 3},
209 {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - DT_DELTA, 4, 0},
210 {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - DT_DELTA, 5, 0},
211 // b needs to be closer to a than to B, so we make this 3*DT_DELTA
212 {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - 3*DT_DELTA, 1, 3},
213 {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - 3*DT_DELTA, 4, 0},
214 {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - 3*DT_DELTA, 5, 0},
215
216 {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour
217 {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour
218 {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour
219 {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour
220 // The C code has had versions of the following 3, keep & update. Should not need these, but...
221 // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns
222 // get skipped instead of mapped to the right hour chars, for example in
223 // DateFormatTest::TestPatternFromSkeleton
224 // IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton
225 // DateIntervalFormatTest::testTicket11985
226 // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton.
227 {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 5*DT_DELTA, 1, 2}, // 12/24 hour no AM/PM
228 {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 6*DT_DELTA, 1, 6}, // 12/24 hour
229 {CAP_C, UDATPG_HOUR_FIELD, DT_NUMERIC + 7*DT_DELTA, 1, 6}, // 12/24 hour with preferred dayPeriods for 12
230
231 {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2},
232
233 {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2},
234 {CAP_A, UDATPG_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000},
235
236 {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC, 1, 1000},
237
238 {LOW_V, UDATPG_ZONE_FIELD, DT_SHORT - 2*DT_DELTA, 1, 0},
239 {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0},
240 {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3},
241 {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0},
242 {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3},
243 {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
244 {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0},
245 {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0},
246 {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
247 {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0},
248 {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0},
249 {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-1 - DT_DELTA, 3, 0},
250 {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-2 - DT_DELTA, 4, 0},
251 {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
252 {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
253 {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
254 {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
255 {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
256 {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
257
258 {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[]
259 };
260
261 static const char* const CLDR_FIELD_APPEND[] = {
262 "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week",
263 "*", "*", "Day", "*", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
264 "Hour", "Minute", "Second", "*", "Timezone"
265 };
266
267 static const char* const CLDR_FIELD_NAME[UDATPG_FIELD_COUNT] = {
268 "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday",
269 "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
270 "hour", "minute", "second", "*", "zone"
271 };
272
273 static const char* const CLDR_FIELD_WIDTH[] = { // [UDATPG_WIDTH_COUNT]
274 "", "-short", "-narrow"
275 };
276
277 // TODO(ticket:13619): remove when definition uncommented in dtptngen.h.
278 static const int32_t UDATPG_WIDTH_COUNT = UDATPG_NARROW + 1;
279 static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE;
280 static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width)
281
282 // For appendItems
283 static const UChar UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
284 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524
285
286 //static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
287
288 static const char DT_DateTimePatternsTag[]="DateTimePatterns";
289 static const char DT_DateTimeCalendarTag[]="calendar";
290 static const char DT_DateTimeGregorianTag[]="gregorian";
291 static const char DT_DateTimeAppendItemsTag[]="appendItems";
292 static const char DT_DateTimeFieldsTag[]="fields";
293 static const char DT_DateTimeAvailableFormatsTag[]="availableFormats";
294 //static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);
295
296 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator)
297 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration)
298 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration)
299
300 DateTimePatternGenerator* U_EXPORT2
301 DateTimePatternGenerator::createInstance(UErrorCode& status) {
302 return createInstance(Locale::getDefault(), status);
303 }
304
305 DateTimePatternGenerator* U_EXPORT2
306 DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status, UBool skipICUData) {
307 if (U_FAILURE(status)) {
308 return nullptr;
309 }
310 LocalPointer<DateTimePatternGenerator> result(
311 new DateTimePatternGenerator(locale, status, skipICUData), status);
312 return U_SUCCESS(status) ? result.orphan() : nullptr;
313 }
314
315 DateTimePatternGenerator* U_EXPORT2
316 DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) {
317 if (U_FAILURE(status)) {
318 return nullptr;
319 }
320 LocalPointer<DateTimePatternGenerator> result(
321 new DateTimePatternGenerator(status), status);
322 return U_SUCCESS(status) ? result.orphan() : nullptr;
323 }
324
325 DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) :
326 skipMatcher(nullptr),
327 fAvailableFormatKeyHash(nullptr),
328 internalErrorCode(U_ZERO_ERROR)
329 {
330 fp = new FormatParser();
331 dtMatcher = new DateTimeMatcher();
332 distanceInfo = new DistanceInfo();
333 patternMap = new PatternMap();
334 if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
335 internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR;
336 }
337 }
338
339 DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status, UBool skipICUData) :
340 skipMatcher(nullptr),
341 fAvailableFormatKeyHash(nullptr),
342 internalErrorCode(U_ZERO_ERROR),
343 pLocale(locale)
344 {
345 fp = new FormatParser();
346 dtMatcher = new DateTimeMatcher();
347 distanceInfo = new DistanceInfo();
348 patternMap = new PatternMap();
349 if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
350 internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR;
351 }
352 else {
353 initData(locale, status, skipICUData);
354 }
355 }
356
357 DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator& other) :
358 UObject(),
359 skipMatcher(nullptr),
360 fAvailableFormatKeyHash(nullptr),
361 internalErrorCode(U_ZERO_ERROR)
362 {
363 fp = new FormatParser();
364 dtMatcher = new DateTimeMatcher();
365 distanceInfo = new DistanceInfo();
366 patternMap = new PatternMap();
367 if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
368 internalErrorCode = U_MEMORY_ALLOCATION_ERROR;
369 }
370 *this=other;
371 }
372
373 DateTimePatternGenerator&
374 DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
375 // reflexive case
376 if (&other == this) {
377 return *this;
378 }
379 internalErrorCode = other.internalErrorCode;
380 pLocale = other.pLocale;
381 fDefaultHourFormatChar = other.fDefaultHourFormatChar;
382 *fp = *(other.fp);
383 dtMatcher->copyFrom(other.dtMatcher->skeleton);
384 *distanceInfo = *(other.distanceInfo);
385 dateTimeFormat = other.dateTimeFormat;
386 decimal = other.decimal;
387 // NUL-terminate for the C API.
388 dateTimeFormat.getTerminatedBuffer();
389 decimal.getTerminatedBuffer();
390 delete skipMatcher;
391 if ( other.skipMatcher == nullptr ) {
392 skipMatcher = nullptr;
393 }
394 else {
395 skipMatcher = new DateTimeMatcher(*other.skipMatcher);
396 if (skipMatcher == nullptr)
397 {
398 internalErrorCode = U_MEMORY_ALLOCATION_ERROR;
399 return *this;
400 }
401 }
402 for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) {
403 appendItemFormats[i] = other.appendItemFormats[i];
404 appendItemFormats[i].getTerminatedBuffer(); // NUL-terminate for the C API.
405 for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) {
406 fieldDisplayNames[i][j] = other.fieldDisplayNames[i][j];
407 fieldDisplayNames[i][j].getTerminatedBuffer(); // NUL-terminate for the C API.
408 }
409 }
410 patternMap->copyFrom(*other.patternMap, internalErrorCode);
411 copyHashtable(other.fAvailableFormatKeyHash, internalErrorCode);
412 return *this;
413 }
414
415
416 UBool
417 DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) const {
418 if (this == &other) {
419 return TRUE;
420 }
421 if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) &&
422 (dateTimeFormat==other.dateTimeFormat) && (decimal==other.decimal)) {
423 for ( int32_t i=0 ; i<UDATPG_FIELD_COUNT; ++i ) {
424 if (appendItemFormats[i] != other.appendItemFormats[i]) {
425 return FALSE;
426 }
427 for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) {
428 if (fieldDisplayNames[i][j] != other.fieldDisplayNames[i][j]) {
429 return FALSE;
430 }
431 }
432 }
433 return TRUE;
434 }
435 else {
436 return FALSE;
437 }
438 }
439
440 UBool
441 DateTimePatternGenerator::operator!=(const DateTimePatternGenerator& other) const {
442 return !operator==(other);
443 }
444
445 DateTimePatternGenerator::~DateTimePatternGenerator() {
446 if (fAvailableFormatKeyHash!=nullptr) {
447 delete fAvailableFormatKeyHash;
448 }
449
450 if (fp != nullptr) delete fp;
451 if (dtMatcher != nullptr) delete dtMatcher;
452 if (distanceInfo != nullptr) delete distanceInfo;
453 if (patternMap != nullptr) delete patternMap;
454 if (skipMatcher != nullptr) delete skipMatcher;
455 }
456
457 namespace {
458
459 UInitOnce initOnce = U_INITONCE_INITIALIZER;
460 UHashtable *localeToAllowedHourFormatsMap = nullptr;
461
462 // Value deleter for hashmap.
463 U_CFUNC void U_CALLCONV deleteAllowedHourFormats(void *ptr) {
464 uprv_free(ptr);
465 }
466
467 // Close hashmap at cleanup.
468 U_CFUNC UBool U_CALLCONV allowedHourFormatsCleanup() {
469 uhash_close(localeToAllowedHourFormatsMap);
470 return TRUE;
471 }
472
473 enum AllowedHourFormat{
474 ALLOWED_HOUR_FORMAT_UNKNOWN = -1,
475 ALLOWED_HOUR_FORMAT_h,
476 ALLOWED_HOUR_FORMAT_H,
477 ALLOWED_HOUR_FORMAT_K, // Added ICU-20383, used by JP
478 ALLOWED_HOUR_FORMAT_k, // Added ICU-20383, not currently used
479 ALLOWED_HOUR_FORMAT_hb,
480 ALLOWED_HOUR_FORMAT_hB,
481 ALLOWED_HOUR_FORMAT_Kb, // Added ICU-20383, not currently used
482 ALLOWED_HOUR_FORMAT_KB, // Added ICU-20383, not currently used
483 // ICU-20383 The following are unlikely and not currently used
484 ALLOWED_HOUR_FORMAT_Hb,
485 ALLOWED_HOUR_FORMAT_HB
486 };
487
488 } // namespace
489
490 void
491 DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status, UBool skipICUData) {
492 //const char *baseLangName = locale.getBaseName(); // unused
493
494 skipMatcher = nullptr;
495 fAvailableFormatKeyHash=nullptr;
496 addCanonicalItems(status);
497 if (!skipICUData) {
498 addICUPatterns(locale, status); // skip to prevent circular dependency when called from SimpleDateFormat::construct
499 }
500 addCLDRData(locale, status);
501 setDateTimeFromCalendar(locale, status);
502 setDecimalSymbols(locale, status);
503 umtx_initOnce(initOnce, loadAllowedHourFormatsData, status);
504 getAllowedHourFormats(locale, status);
505 // If any of the above methods failed then the object is in an invalid state.
506 internalErrorCode = status;
507 } // DateTimePatternGenerator::initData
508
509 namespace {
510
511 struct AllowedHourFormatsSink : public ResourceSink {
512 // Initialize sub-sinks.
513 AllowedHourFormatsSink() {}
514 virtual ~AllowedHourFormatsSink();
515
516 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
517 UErrorCode &errorCode) {
518 ResourceTable timeData = value.getTable(errorCode);
519 if (U_FAILURE(errorCode)) { return; }
520 for (int32_t i = 0; timeData.getKeyAndValue(i, key, value); ++i) {
521 const char *regionOrLocale = key;
522 ResourceTable formatList = value.getTable(errorCode);
523 if (U_FAILURE(errorCode)) { return; }
524 // below we construct a list[] that has an entry for the "preferred" value at [0],
525 // followed by 1 or more entries for the "allowed" values, terminated with an
526 // entry for ALLOWED_HOUR_FORMAT_UNKNOWN (not included in length below)
527 LocalMemory<int32_t> list;
528 int32_t length = 0;
529 int32_t preferredFormat = ALLOWED_HOUR_FORMAT_UNKNOWN;
530 for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) {
531 if (uprv_strcmp(key, "allowed") == 0) {
532 if (value.getType() == URES_STRING) {
533 length = 2; // 1 preferred to add later, 1 allowed to add now
534 if (list.allocateInsteadAndReset(length + 1) == nullptr) {
535 errorCode = U_MEMORY_ALLOCATION_ERROR;
536 return;
537 }
538 list[1] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
539 }
540 else {
541 ResourceArray allowedFormats = value.getArray(errorCode);
542 length = allowedFormats.getSize() + 1; // 1 preferred, getSize allowed
543 if (list.allocateInsteadAndReset(length + 1) == nullptr) {
544 errorCode = U_MEMORY_ALLOCATION_ERROR;
545 return;
546 }
547 for (int32_t k = 1; k < length; ++k) {
548 allowedFormats.getValue(k-1, value);
549 list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
550 }
551 }
552 } else if (uprv_strcmp(key, "preferred") == 0) {
553 preferredFormat = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
554 }
555 }
556 if (length > 1) {
557 list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: list[1];
558 } else {
559 // fallback handling for missing data
560 length = 2; // 1 preferred, 1 allowed
561 if (list.allocateInsteadAndReset(length + 1) == nullptr) {
562 errorCode = U_MEMORY_ALLOCATION_ERROR;
563 return;
564 }
565 list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: ALLOWED_HOUR_FORMAT_H;
566 list[1] = list[0];
567 }
568 list[length] = ALLOWED_HOUR_FORMAT_UNKNOWN;
569 // At this point list[] will have at least two non-ALLOWED_HOUR_FORMAT_UNKNOWN entries,
570 // followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
571 uhash_put(localeToAllowedHourFormatsMap, const_cast<char *>(regionOrLocale), list.orphan(), &errorCode);
572 if (U_FAILURE(errorCode)) { return; }
573 }
574 }
575
576 AllowedHourFormat getHourFormatFromUnicodeString(const UnicodeString &s) {
577 if (s.length() == 1) {
578 if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; }
579 if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; }
580 if (s[0] == CAP_K) { return ALLOWED_HOUR_FORMAT_K; }
581 if (s[0] == LOW_K) { return ALLOWED_HOUR_FORMAT_k; }
582 } else if (s.length() == 2) {
583 if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; }
584 if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; }
585 if (s[0] == CAP_K && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Kb; }
586 if (s[0] == CAP_K && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_KB; }
587 if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; }
588 if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; }
589 }
590
591 return ALLOWED_HOUR_FORMAT_UNKNOWN;
592 }
593 };
594
595 } // namespace
596
597 AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
598
599 U_CFUNC void U_CALLCONV DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) {
600 if (U_FAILURE(status)) { return; }
601 localeToAllowedHourFormatsMap = uhash_open(
602 uhash_hashChars, uhash_compareChars, nullptr, &status);
603 if (U_FAILURE(status)) { return; }
604
605 uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats);
606 ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup);
607
608 LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status));
609 if (U_FAILURE(status)) { return; }
610
611 AllowedHourFormatsSink sink;
612 // TODO: Currently in the enumeration each table allocates a new array.
613 // Try to reduce the number of memory allocations. Consider storing a
614 // UVector32 with the concatenation of all of the sub-arrays, put the start index
615 // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
616 // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
617 // object. Remember to clean up the vector, too.
618 ures_getAllItemsWithFallback(rb.getAlias(), "timeData", sink, status);
619 }
620
621 static int32_t* getAllowedHourFormatsLangCountry(const char* language, const char* country, UErrorCode& status) {
622 CharString langCountry;
623 langCountry.append(language, status);
624 langCountry.append('_', status);
625 langCountry.append(country, status);
626
627 int32_t* allowedFormats;
628 allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data());
629 if (allowedFormats == nullptr) {
630 allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast<char *>(country));
631 }
632
633 return allowedFormats;
634 }
635
636 void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
637 if (U_FAILURE(status)) { return; }
638
639 const char *language = locale.getLanguage();
640 const char *country = locale.getCountry();
641 const char *locName = locale.getName(); // Apple addition
642 if (*locName==0 || uprv_strcmp(locName,"root")==0 || uprv_strcmp(locName,"und")==0) { // Apple addition
643 language = "und";
644 country = "001";
645 }
646 Locale maxLocale; // must be here for correct lifetime
647 if (*language == '\0' || *country == '\0') {
648 maxLocale = locale;
649 UErrorCode localStatus = U_ZERO_ERROR;
650 maxLocale.addLikelySubtags(localStatus);
651 if (U_SUCCESS(localStatus)) {
652 language = maxLocale.getLanguage();
653 country = maxLocale.getCountry();
654 }
655 }
656 if (*language == '\0') {
657 // Unexpected, but fail gracefully
658 language = "und";
659 }
660 if (*country == '\0') {
661 country = "001";
662 }
663
664 int32_t* allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);
665
666 // Check if the region has an alias
667 if (allowedFormats == nullptr) {
668 UErrorCode localStatus = U_ZERO_ERROR;
669 const Region* region = Region::getInstance(country, localStatus);
670 if (U_SUCCESS(localStatus)) {
671 country = region->getRegionCode(); // the real region code
672 allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);
673 }
674 }
675
676 if (allowedFormats != nullptr) { // Lookup is successful
677 // Here allowedFormats points to a list consisting of key for preferredFormat,
678 // followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
679 switch (allowedFormats[0]) {
680 case ALLOWED_HOUR_FORMAT_h: fDefaultHourFormatChar = LOW_H; break;
681 case ALLOWED_HOUR_FORMAT_H: fDefaultHourFormatChar = CAP_H; break;
682 case ALLOWED_HOUR_FORMAT_K: fDefaultHourFormatChar = CAP_K; break;
683 case ALLOWED_HOUR_FORMAT_k: fDefaultHourFormatChar = LOW_K; break;
684 default: fDefaultHourFormatChar = CAP_H; break;
685 }
686 for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) {
687 fAllowedHourFormats[i] = allowedFormats[i + 1];
688 if (fAllowedHourFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) {
689 break;
690 }
691 }
692 } else { // Lookup failed, twice
693 fDefaultHourFormatChar = CAP_H;
694 fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H;
695 fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN;
696 }
697 }
698
699 UnicodeString
700 DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode&
701 /*status*/) {
702 FormatParser fp2;
703 DateTimeMatcher matcher;
704 PtnSkeleton localSkeleton;
705 matcher.set(pattern, &fp2, localSkeleton);
706 return localSkeleton.getSkeleton();
707 }
708
709 UnicodeString
710 DateTimePatternGenerator::staticGetSkeleton(
711 const UnicodeString& pattern, UErrorCode& /*status*/) {
712 FormatParser fp;
713 DateTimeMatcher matcher;
714 PtnSkeleton localSkeleton;
715 matcher.set(pattern, &fp, localSkeleton);
716 return localSkeleton.getSkeleton();
717 }
718
719 UnicodeString
720 DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) {
721 FormatParser fp2;
722 DateTimeMatcher matcher;
723 PtnSkeleton localSkeleton;
724 matcher.set(pattern, &fp2, localSkeleton);
725 return localSkeleton.getBaseSkeleton();
726 }
727
728 UnicodeString
729 DateTimePatternGenerator::staticGetBaseSkeleton(
730 const UnicodeString& pattern, UErrorCode& /*status*/) {
731 FormatParser fp;
732 DateTimeMatcher matcher;
733 PtnSkeleton localSkeleton;
734 matcher.set(pattern, &fp, localSkeleton);
735 return localSkeleton.getBaseSkeleton();
736 }
737
738 void
739 DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) {
740 if (U_FAILURE(status)) { return; }
741 UnicodeString dfPattern;
742 UnicodeString conflictingString;
743 DateFormat* df;
744
745 // Load with ICU patterns
746 for (int32_t i=DateFormat::kFull; i<=DateFormat::kShort; i++) {
747 DateFormat::EStyle style = (DateFormat::EStyle)i;
748 df = DateFormat::createDateInstance(style, locale);
749 SimpleDateFormat* sdf;
750 if (df != nullptr && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != nullptr) {
751 sdf->toPattern(dfPattern);
752 addPattern(dfPattern, FALSE, conflictingString, status);
753 }
754 // TODO Maybe we should return an error when the date format isn't simple.
755 delete df;
756 if (U_FAILURE(status)) { return; }
757
758 df = DateFormat::createTimeInstance(style, locale);
759 if (df != nullptr && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != nullptr) {
760 sdf->toPattern(dfPattern);
761 addPattern(dfPattern, FALSE, conflictingString, status);
762
763 // TODO: C++ and Java are inconsistent (see #12568).
764 // C++ uses MEDIUM, but Java uses SHORT.
765 if ( i==DateFormat::kShort && !dfPattern.isEmpty() ) {
766 consumeShortTimePattern(dfPattern, status);
767 }
768 }
769 // TODO Maybe we should return an error when the date format isn't simple.
770 delete df;
771 if (U_FAILURE(status)) { return; }
772 }
773 }
774
775 void
776 DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status) {
777 UnicodeString conflictingString;
778
779 fp->set(hackPattern);
780 UnicodeString mmss;
781 UBool gotMm=FALSE;
782 for (int32_t i=0; i<fp->itemNumber; ++i) {
783 UnicodeString field = fp->items[i];
784 if ( fp->isQuoteLiteral(field) ) {
785 if ( gotMm ) {
786 UnicodeString quoteLiteral;
787 fp->getQuoteLiteral(quoteLiteral, &i);
788 mmss += quoteLiteral;
789 }
790 }
791 else {
792 if (fp->isPatternSeparator(field) && gotMm) {
793 mmss+=field;
794 }
795 else {
796 UChar ch=field.charAt(0);
797 if (ch==LOW_M) {
798 gotMm=TRUE;
799 mmss+=field;
800 }
801 else {
802 if (ch==LOW_S) {
803 if (!gotMm) {
804 break;
805 }
806 mmss+= field;
807 addPattern(mmss, FALSE, conflictingString, status);
808 break;
809 }
810 else {
811 if (gotMm || ch==LOW_Z || ch==CAP_Z || ch==LOW_V || ch==CAP_V) {
812 break;
813 }
814 }
815 }
816 }
817 }
818 }
819 }
820
821 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
822
823 void
824 DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString& destination, UErrorCode& err) {
825 destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default
826 if ( U_SUCCESS(err) ) {
827 // HACK to maintain backward compatibility with old behavior-- the Calendar API used to return "japanese"
828 // as the default calendar for ja_JP_TRADTITIONAL, but now it returns "gregorian".
829 // But ures_getFunctionalEquivalent(), which this function used to call, still returned "japanese". To keep
830 // the old unit tests passing, we special-case this situation to still return "japanese" for ja_JP_TRADITIONAL.
831 if (uprv_strcmp(locale.getName(), "ja_JP_TRADITIONAL") == 0) {
832 destination.clear().append("japanese", -1, err);
833 } else {
834 char calType[50];
835 Calendar::getCalendarTypeFromLocale(locale, calType, 50, err);
836 if (U_SUCCESS(err)) {
837 destination.clear().append(calType, -1, err);
838 }
839 }
840 }
841 }
842
843 void
844 DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern,
845 UErrorCode& status) {
846 if (U_FAILURE(status)) { return; }
847 // ICU-20383 No longer set fDefaultHourFormatChar to the hour format character from
848 // this pattern; instead it is set from localeToAllowedHourFormatsMap which now
849 // includes entries for both preferred and allowed formats.
850
851 // HACK for hh:ss
852 hackTimes(shortTimePattern, status);
853 }
854
855 struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink {
856
857 // Destination for data, modified via setters.
858 DateTimePatternGenerator& dtpg;
859
860 AppendItemFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
861 virtual ~AppendItemFormatsSink();
862
863 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
864 UErrorCode &errorCode) {
865 ResourceTable itemsTable = value.getTable(errorCode);
866 if (U_FAILURE(errorCode)) { return; }
867 for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
868 UDateTimePatternField field = dtpg.getAppendFormatNumber(key);
869 if (field == UDATPG_FIELD_COUNT) { continue; }
870 const UnicodeString& valueStr = value.getUnicodeString(errorCode);
871 if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) {
872 dtpg.setAppendItemFormat(field, valueStr);
873 }
874 }
875 }
876
877 void fillInMissing() {
878 UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias.
879 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
880 UDateTimePatternField field = (UDateTimePatternField)i;
881 if (dtpg.getAppendItemFormat(field).isEmpty()) {
882 dtpg.setAppendItemFormat(field, defaultItemFormat);
883 }
884 }
885 }
886 };
887
888 struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {
889
890 // Destination for data, modified via setters.
891 DateTimePatternGenerator& dtpg;
892
893 AppendItemNamesSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
894 virtual ~AppendItemNamesSink();
895
896 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
897 UErrorCode &errorCode) {
898 ResourceTable itemsTable = value.getTable(errorCode);
899 if (U_FAILURE(errorCode)) { return; }
900 for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
901 UDateTimePGDisplayWidth width;
902 UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width);
903 if (field == UDATPG_FIELD_COUNT) { continue; }
904 ResourceTable detailsTable = value.getTable(errorCode);
905 if (U_FAILURE(errorCode)) { return; }
906 for (int32_t j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) {
907 if (uprv_strcmp(key, "dn") != 0) { continue; }
908 const UnicodeString& valueStr = value.getUnicodeString(errorCode);
909 if (dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) {
910 dtpg.setFieldDisplayName(field,width,valueStr);
911 }
912 break;
913 }
914 }
915 }
916
917 void fillInMissing() {
918 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
919 UnicodeString& valueStr = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, UDATPG_WIDE);
920 if (valueStr.isEmpty()) {
921 valueStr = CAP_F;
922 U_ASSERT(i < 20);
923 if (i < 10) {
924 // F0, F1, ..., F9
925 valueStr += (UChar)(i+0x30);
926 } else {
927 // F10, F11, ...
928 valueStr += (UChar)0x31;
929 valueStr += (UChar)(i-10 + 0x30);
930 }
931 // NUL-terminate for the C API.
932 valueStr.getTerminatedBuffer();
933 }
934 for (int32_t j = 1; j < UDATPG_WIDTH_COUNT; j++) {
935 UnicodeString& valueStr2 = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)j);
936 if (valueStr2.isEmpty()) {
937 valueStr2 = dtpg.getFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)(j-1));
938 }
939 }
940 }
941 }
942 };
943
944 struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink {
945
946 // Destination for data, modified via setters.
947 DateTimePatternGenerator& dtpg;
948
949 // UBool flag indicating whether to populate the generator with all patterns or just date patterns with numeric cores
950 UBool onlyDatesWithNumericCores;
951
952 // Temporary variable, required for calling addPatternWithSkeleton.
953 UnicodeString conflictingPattern;
954
955 AvailableFormatsSink(DateTimePatternGenerator& _dtpg, UBool onlyDatesWithNumericCores) : dtpg(_dtpg), onlyDatesWithNumericCores(onlyDatesWithNumericCores) {}
956 virtual ~AvailableFormatsSink();
957
958 virtual void put(const char *key, ResourceValue &value, UBool isRoot,
959 UErrorCode &errorCode) {
960 ResourceTable itemsTable = value.getTable(errorCode);
961 if (U_FAILURE(errorCode)) { return; }
962 for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
963 UErrorCode valueErr = U_ZERO_ERROR; // if the resource value isn't a string, skip it without returning an error to the caller
964 const UnicodeString formatKey(key, -1, US_INV);
965 UnicodeString formatValue = value.getUnicodeString(valueErr);
966 if (U_SUCCESS(valueErr) && !dtpg.isAvailableFormatSet(formatKey) && (!onlyDatesWithNumericCores || datePatternHasNumericCore(formatValue))) {
967 // if the date pattern generator's locale is a LTR locale, strip out any Unicode right-to-left marks
968 // (if the pattern string came from a RTL locale, it may have RLMs around some separator characters
969 // to get them to lay out correctly, but we don't want that in an LTR context)
970 if (!dtpg.pLocale.isRightToLeft()) {
971 formatValue.findAndReplace(UnicodeString(u'\u200f'), UnicodeString());
972 }
973 dtpg.setAvailableFormat(formatKey, errorCode);
974 // Add pattern with its associated skeleton. Override any duplicate
975 // derived from std patterns, but not a previous availableFormats entry:
976 conflictingPattern.remove();
977 dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode);
978 }
979 }
980 }
981 };
982
983 // Virtual destructors must be defined out of line.
984 DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
985 DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
986 DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}
987
988 void
989 DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) {
990 if (U_FAILURE(errorCode)) { return; }
991 UnicodeString rbPattern, value, field;
992 CharString path;
993
994 UBool hasCountryFallbackResource = FALSE;
995 LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &errorCode));
996 LocalUResourceBundlePointer countryRB(ures_openWithCountryFallback(nullptr, locale.getName(), &hasCountryFallbackResource, &errorCode));
997 if (U_FAILURE(errorCode)) { return; }
998
999 CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well
1000 getCalendarTypeToUse(locale, calendarTypeToUse, errorCode);
1001 if (U_FAILURE(errorCode)) { return; }
1002
1003 // Local err to ignore resource not found exceptions
1004 UErrorCode err = U_ZERO_ERROR;
1005
1006 // Load append item formats.
1007 AppendItemFormatsSink appendItemFormatsSink(*this);
1008 path.clear()
1009 .append(DT_DateTimeCalendarTag, errorCode)
1010 .append('/', errorCode)
1011 .append(calendarTypeToUse, errorCode)
1012 .append('/', errorCode)
1013 .append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems
1014 if (U_FAILURE(errorCode)) { return; }
1015 ures_getAllItemsWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err);
1016 appendItemFormatsSink.fillInMissing();
1017
1018 // Load CLDR item names.
1019 err = U_ZERO_ERROR;
1020 AppendItemNamesSink appendItemNamesSink(*this);
1021 ures_getAllItemsWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err);
1022 appendItemNamesSink.fillInMissing();
1023
1024 // Load the available formats from CLDR.
1025 err = U_ZERO_ERROR;
1026 initHashtable(errorCode);
1027 if (U_FAILURE(errorCode)) { return; }
1028 path.clear()
1029 .append(DT_DateTimeCalendarTag, errorCode)
1030 .append('/', errorCode)
1031 .append(calendarTypeToUse, errorCode)
1032 .append('/', errorCode)
1033 .append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats
1034 if (U_FAILURE(errorCode)) { return; }
1035 if (hasCountryFallbackResource) {
1036 AvailableFormatsSink countryAvailableFormatsSink(*this, TRUE);
1037 ures_getAllItemsWithFallback(countryRB.getAlias(), path.data(), countryAvailableFormatsSink, err);
1038 }
1039 AvailableFormatsSink availableFormatsSink(*this, FALSE);
1040 ures_getAllItemsWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err);
1041 }
1042
1043 void
1044 DateTimePatternGenerator::initHashtable(UErrorCode& err) {
1045 if (U_FAILURE(err)) { return; }
1046 if (fAvailableFormatKeyHash!=nullptr) {
1047 return;
1048 }
1049 LocalPointer<Hashtable> hash(new Hashtable(FALSE, err), err);
1050 if (U_SUCCESS(err)) {
1051 fAvailableFormatKeyHash = hash.orphan();
1052 }
1053 }
1054
1055 void
1056 DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) {
1057 appendItemFormats[field] = value;
1058 // NUL-terminate for the C API.
1059 appendItemFormats[field].getTerminatedBuffer();
1060 }
1061
1062 const UnicodeString&
1063 DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const {
1064 return appendItemFormats[field];
1065 }
1066
1067 void
1068 DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) {
1069 setFieldDisplayName(field, UDATPG_WIDTH_APPENDITEM, value);
1070 }
1071
1072 const UnicodeString&
1073 DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const {
1074 return fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
1075 }
1076
1077 void
1078 DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width, const UnicodeString& value) {
1079 fieldDisplayNames[field][width] = value;
1080 // NUL-terminate for the C API.
1081 fieldDisplayNames[field][width].getTerminatedBuffer();
1082 }
1083
1084 UnicodeString
1085 DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const {
1086 return fieldDisplayNames[field][width];
1087 }
1088
1089 UnicodeString&
1090 DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) {
1091 return fieldDisplayNames[field][width];
1092 }
1093
1094 void
1095 DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) {
1096 value = SINGLE_QUOTE;
1097 value += fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
1098 value += SINGLE_QUOTE;
1099 }
1100
1101 UnicodeString
1102 DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) {
1103 return getBestPattern(patternForm, UDATPG_MATCH_NO_OPTIONS, status);
1104 }
1105
1106 UnicodeString
1107 DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDateTimePatternMatchOptions options, UErrorCode& status) {
1108 if (U_FAILURE(status)) {
1109 return UnicodeString();
1110 }
1111 if (U_FAILURE(internalErrorCode)) {
1112 status = internalErrorCode;
1113 return UnicodeString();
1114 }
1115 const UnicodeString *bestPattern = nullptr;
1116 UnicodeString dtFormat;
1117 UnicodeString resultPattern;
1118 int32_t flags = kDTPGNoFlags;
1119
1120 int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
1121 int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
1122
1123 // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
1124 UnicodeString patternFormMapped = mapSkeletonMetacharacters(patternForm, &flags, options, status);
1125 if (U_FAILURE(status)) {
1126 return UnicodeString();
1127 }
1128
1129 resultPattern.remove();
1130 dtMatcher->set(patternFormMapped, fp);
1131 const PtnSkeleton* specifiedSkeleton = nullptr;
1132 bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, status, &specifiedSkeleton);
1133
1134 // getBestRaw() might return a pattern with an era field in it, even if the skeleton didn't specifically ask for it.
1135 // Check for that and take it out of distanceInfo->missingFieldMask so that we don't end up adding a SECOND era
1136 // field by mistake
1137 if (bestPattern->indexOf(u'G') != -1) {
1138 distanceInfo->missingFieldMask &= ~(1 << UDATPG_ERA_FIELD);
1139 }
1140
1141 if (U_FAILURE(status)) {
1142 return UnicodeString();
1143 }
1144
1145 if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) {
1146 resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options);
1147
1148 return resultPattern;
1149 }
1150 int32_t neededFields = dtMatcher->getFieldMask();
1151 UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, status, options);
1152 UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, status, options);
1153 if (U_FAILURE(status)) {
1154 return UnicodeString();
1155 }
1156 if (datePattern.length()==0) {
1157 if (timePattern.length()==0) {
1158 resultPattern.remove();
1159 }
1160 else {
1161 return timePattern;
1162 }
1163 }
1164 if (timePattern.length()==0) {
1165 return datePattern;
1166 }
1167 resultPattern.remove();
1168 status = U_ZERO_ERROR;
1169 dtFormat=getDateTimeFormat();
1170 SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status);
1171 return resultPattern;
1172 }
1173
1174 /*
1175 * Map a skeleton that may have metacharacters jJC to one without, by replacing
1176 * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B
1177 * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in
1178 * turn depends on initData having been run). This method also updates the flags
1179 * as necessary. Returns the updated skeleton.
1180 */
1181 UnicodeString
1182 DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UDateTimePatternMatchOptions options, UErrorCode& status) {
1183 UnicodeString patternFormMapped;
1184 patternFormMapped.remove();
1185 UChar hourFormatSkeletonCharForLowJ = fDefaultHourFormatChar;
1186 switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
1187 case UADATPG_FORCE_12_HOUR_CYCLE: hourFormatSkeletonCharForLowJ = LOW_H; break;
1188 case UADATPG_FORCE_24_HOUR_CYCLE: hourFormatSkeletonCharForLowJ = CAP_H; break;
1189 default: break;
1190 }
1191 UBool inQuoted = FALSE;
1192 int32_t patPos, patLen = patternForm.length();
1193 for (patPos = 0; patPos < patLen; patPos++) {
1194 UChar patChr = patternForm.charAt(patPos);
1195 if (patChr == SINGLE_QUOTE) {
1196 inQuoted = !inQuoted;
1197 } else if (!inQuoted) {
1198 // Handle special mappings for 'j' and 'C' in which fields lengths
1199 // 1,3,5 => hour field length 1
1200 // 2,4,6 => hour field length 2
1201 // 1,2 => abbreviated dayPeriod (field length 1..3)
1202 // 3,4 => long dayPeriod (field length 4)
1203 // 5,6 => narrow dayPeriod (field length 5)
1204 if (patChr == LOW_J || patChr == CAP_C) {
1205 int32_t extraLen = 0; // 1 less than total field length
1206 while (patPos+1 < patLen && patternForm.charAt(patPos+1)==patChr) {
1207 extraLen++;
1208 patPos++;
1209 }
1210 int32_t hourLen = 1 + (extraLen & 1);
1211 int32_t dayPeriodLen = (extraLen < 2)? 1: 3 + (extraLen >> 1);
1212 UChar hourChar = LOW_H;
1213 UChar dayPeriodChar = LOW_A;
1214 if (patChr == LOW_J) {
1215 hourChar = hourFormatSkeletonCharForLowJ;
1216 } else {
1217 AllowedHourFormat bestAllowed;
1218 if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
1219 bestAllowed = (AllowedHourFormat)fAllowedHourFormats[0];
1220 } else {
1221 status = U_INVALID_FORMAT_ERROR;
1222 return UnicodeString();
1223 }
1224 if (bestAllowed == ALLOWED_HOUR_FORMAT_H || bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_Hb) {
1225 hourChar = CAP_H;
1226 } else if (bestAllowed == ALLOWED_HOUR_FORMAT_K || bestAllowed == ALLOWED_HOUR_FORMAT_KB || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) {
1227 hourChar = CAP_K;
1228 } else if (bestAllowed == ALLOWED_HOUR_FORMAT_k) {
1229 hourChar = LOW_K;
1230 }
1231 // in #13183 just add b/B to skeleton, no longer need to set special flags
1232 if (bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_hB || bestAllowed == ALLOWED_HOUR_FORMAT_KB) {
1233 dayPeriodChar = CAP_B;
1234 } else if (bestAllowed == ALLOWED_HOUR_FORMAT_Hb || bestAllowed == ALLOWED_HOUR_FORMAT_hb || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) {
1235 dayPeriodChar = LOW_B;
1236 }
1237 }
1238 if (hourChar==CAP_H || hourChar==LOW_K) {
1239 dayPeriodLen = 0;
1240 }
1241 while (dayPeriodLen-- > 0) {
1242 patternFormMapped.append(dayPeriodChar);
1243 }
1244 while (hourLen-- > 0) {
1245 patternFormMapped.append(hourChar);
1246 }
1247 } else if (patChr == CAP_J) {
1248 // Get pattern for skeleton with H, then replace H or k
1249 // with fDefaultHourFormatChar (if different)
1250 patternFormMapped.append(CAP_H);
1251 *flags |= kDTPGSkeletonUsesCapJ;
1252 } else {
1253 patternFormMapped.append(patChr);
1254 }
1255 }
1256 }
1257 return patternFormMapped;
1258 }
1259
1260 UnicodeString
1261 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
1262 const UnicodeString& skeleton,
1263 UErrorCode& status) {
1264 return replaceFieldTypes(pattern, skeleton, UDATPG_MATCH_NO_OPTIONS, status);
1265 }
1266
1267 UnicodeString
1268 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
1269 const UnicodeString& skeleton,
1270 UDateTimePatternMatchOptions options,
1271 UErrorCode& status) {
1272 if (U_FAILURE(status)) {
1273 return UnicodeString();
1274 }
1275 if (U_FAILURE(internalErrorCode)) {
1276 status = internalErrorCode;
1277 return UnicodeString();
1278 }
1279 dtMatcher->set(skeleton, fp);
1280 UnicodeString result = adjustFieldTypes(pattern, nullptr, kDTPGNoFlags, options);
1281 return result;
1282 }
1283
1284 void
1285 DateTimePatternGenerator::setDecimal(const UnicodeString& newDecimal) {
1286 this->decimal = newDecimal;
1287 // NUL-terminate for the C API.
1288 this->decimal.getTerminatedBuffer();
1289 }
1290
1291 const UnicodeString&
1292 DateTimePatternGenerator::getDecimal() const {
1293 return decimal;
1294 }
1295
1296 void
1297 DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) {
1298 if (U_FAILURE(status)) { return; }
1299 UnicodeString conflictingPattern;
1300
1301 for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) {
1302 if (Canonical_Items[i] > 0) {
1303 addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
1304 }
1305 if (U_FAILURE(status)) { return; }
1306 }
1307 }
1308
1309 void
1310 DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) {
1311 dateTimeFormat = dtFormat;
1312 // NUL-terminate for the C API.
1313 dateTimeFormat.getTerminatedBuffer();
1314 }
1315
1316 const UnicodeString&
1317 DateTimePatternGenerator::getDateTimeFormat() const {
1318 return dateTimeFormat;
1319 }
1320
1321 void
1322 DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCode& status) {
1323 if (U_FAILURE(status)) { return; }
1324
1325 const UChar *resStr;
1326 int32_t resStrLen = 0;
1327
1328 LocalPointer<Calendar> fCalendar(Calendar::createInstance(locale, status), status);
1329 if (U_FAILURE(status)) { return; }
1330
1331 LocalUResourceBundlePointer calData(ures_open(nullptr, locale.getBaseName(), &status));
1332 if (U_FAILURE(status)) { return; }
1333 ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status);
1334 if (U_FAILURE(status)) { return; }
1335
1336 LocalUResourceBundlePointer dateTimePatterns;
1337 if (fCalendar->getType() != nullptr && *fCalendar->getType() != '\0'
1338 && uprv_strcmp(fCalendar->getType(), DT_DateTimeGregorianTag) != 0) {
1339 dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), fCalendar->getType(),
1340 nullptr, &status));
1341 ures_getByKeyWithFallback(dateTimePatterns.getAlias(), DT_DateTimePatternsTag,
1342 dateTimePatterns.getAlias(), &status);
1343 }
1344
1345 if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) {
1346 status = U_ZERO_ERROR;
1347 dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag,
1348 dateTimePatterns.orphan(), &status));
1349 ures_getByKeyWithFallback(dateTimePatterns.getAlias(), DT_DateTimePatternsTag,
1350 dateTimePatterns.getAlias(), &status);
1351 }
1352 if (U_FAILURE(status)) { return; }
1353
1354 if (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTime)
1355 {
1356 status = U_INVALID_FORMAT_ERROR;
1357 return;
1358 }
1359 resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), (int32_t)DateFormat::kDateTime, &resStrLen, &status);
1360 setDateTimeFormat(UnicodeString(TRUE, resStr, resStrLen));
1361 }
1362
1363 void
1364 DateTimePatternGenerator::setDecimalSymbols(const Locale& locale, UErrorCode& status) {
1365 DecimalFormatSymbols dfs = DecimalFormatSymbols(locale, status);
1366 if(U_SUCCESS(status)) {
1367 decimal = dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
1368 // NUL-terminate for the C API.
1369 decimal.getTerminatedBuffer();
1370 }
1371 }
1372
1373 UDateTimePatternConflict
1374 DateTimePatternGenerator::addPattern(
1375 const UnicodeString& pattern,
1376 UBool override,
1377 UnicodeString &conflictingPattern,
1378 UErrorCode& status)
1379 {
1380 if (U_FAILURE(internalErrorCode)) {
1381 status = internalErrorCode;
1382 return UDATPG_NO_CONFLICT;
1383 }
1384
1385 return addPatternWithSkeleton(pattern, nullptr, override, conflictingPattern, status);
1386 }
1387
1388 // For DateTimePatternGenerator::addPatternWithSkeleton -
1389 // If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
1390 // 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern.
1391 // 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
1392 // (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
1393 // parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
1394 // specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
1395 // derived (i.e. entries derived from the standard date/time patters for the specified locale).
1396 // 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a
1397 // specified skeleton (which sets a new field in the PtnElem in the PatternMap).
1398 UDateTimePatternConflict
1399 DateTimePatternGenerator::addPatternWithSkeleton(
1400 const UnicodeString& pattern,
1401 const UnicodeString* skeletonToUse,
1402 UBool override,
1403 UnicodeString& conflictingPattern,
1404 UErrorCode& status)
1405 {
1406 if (U_FAILURE(internalErrorCode)) {
1407 status = internalErrorCode;
1408 return UDATPG_NO_CONFLICT;
1409 }
1410
1411 UnicodeString basePattern;
1412 PtnSkeleton skeleton;
1413 UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT;
1414
1415 DateTimeMatcher matcher;
1416 if ( skeletonToUse == nullptr ) {
1417 matcher.set(pattern, fp, skeleton);
1418 matcher.getBasePattern(basePattern);
1419 } else {
1420 matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930
1421 matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse;
1422 }
1423 // We only care about base conflicts - and replacing the pattern associated with a base - if:
1424 // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
1425 // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
1426 // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
1427 // if we are getting here from a subsequent call to addPattern).
1428 // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
1429 // availableFormats items from root, which should not override any previous entry with the same base.
1430 UBool entryHadSpecifiedSkeleton;
1431 const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton);
1432 if (duplicatePattern != nullptr && (!entryHadSpecifiedSkeleton || (skeletonToUse != nullptr && !override))) {
1433 conflictingStatus = UDATPG_BASE_CONFLICT;
1434 conflictingPattern = *duplicatePattern;
1435 if (!override) {
1436 return conflictingStatus;
1437 }
1438 }
1439 // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
1440 // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
1441 // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
1442 // the previously-specified conflicting item.
1443 const PtnSkeleton* entrySpecifiedSkeleton = nullptr;
1444 duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton);
1445 if (duplicatePattern != nullptr ) {
1446 conflictingStatus = UDATPG_CONFLICT;
1447 conflictingPattern = *duplicatePattern;
1448 if (!override || (skeletonToUse != nullptr && entrySpecifiedSkeleton != nullptr)) {
1449 return conflictingStatus;
1450 }
1451 }
1452 patternMap->add(basePattern, skeleton, pattern, skeletonToUse != nullptr, status);
1453 if(U_FAILURE(status)) {
1454 return conflictingStatus;
1455 }
1456
1457 return UDATPG_NO_CONFLICT;
1458 }
1459
1460
1461 UDateTimePatternField
1462 DateTimePatternGenerator::getAppendFormatNumber(const char* field) const {
1463 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
1464 if (uprv_strcmp(CLDR_FIELD_APPEND[i], field)==0) {
1465 return (UDateTimePatternField)i;
1466 }
1467 }
1468 return UDATPG_FIELD_COUNT;
1469 }
1470
1471 UDateTimePatternField
1472 DateTimePatternGenerator::getFieldAndWidthIndices(const char* key, UDateTimePGDisplayWidth* widthP) const {
1473 char cldrFieldKey[UDATPG_FIELD_KEY_MAX + 1];
1474 uprv_strncpy(cldrFieldKey, key, UDATPG_FIELD_KEY_MAX);
1475 cldrFieldKey[UDATPG_FIELD_KEY_MAX]=0; // ensure termination
1476 *widthP = UDATPG_WIDE;
1477 char* hyphenPtr = uprv_strchr(cldrFieldKey, '-');
1478 if (hyphenPtr) {
1479 for (int32_t i=UDATPG_WIDTH_COUNT-1; i>0; --i) {
1480 if (uprv_strcmp(CLDR_FIELD_WIDTH[i], hyphenPtr)==0) {
1481 *widthP=(UDateTimePGDisplayWidth)i;
1482 break;
1483 }
1484 }
1485 *hyphenPtr = 0; // now delete width portion of key
1486 }
1487 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
1488 if (uprv_strcmp(CLDR_FIELD_NAME[i],cldrFieldKey)==0) {
1489 return (UDateTimePatternField)i;
1490 }
1491 }
1492 return UDATPG_FIELD_COUNT;
1493 }
1494
1495 const UnicodeString*
1496 DateTimePatternGenerator::getBestRaw(DateTimeMatcher& source,
1497 int32_t includeMask,
1498 DistanceInfo* missingFields,
1499 UErrorCode &status,
1500 const PtnSkeleton** specifiedSkeletonPtr) {
1501 int32_t bestDistance = 0x7fffffff;
1502 DistanceInfo tempInfo;
1503 const UnicodeString *bestPattern=nullptr;
1504 const PtnSkeleton* specifiedSkeleton=nullptr;
1505
1506 PatternMapIterator it(status);
1507 if (U_FAILURE(status)) { return nullptr; }
1508
1509 for (it.set(*patternMap); it.hasNext(); ) {
1510 DateTimeMatcher trial = it.next();
1511 if (trial.equals(skipMatcher)) {
1512 continue;
1513 }
1514 int32_t distance=source.getDistance(trial, includeMask, tempInfo);
1515 if (distance<bestDistance) {
1516 bestDistance=distance;
1517 bestPattern=patternMap->getPatternFromSkeleton(*trial.getSkeletonPtr(), &specifiedSkeleton);
1518 missingFields->setTo(tempInfo);
1519 if (distance==0) {
1520 break;
1521 }
1522 }
1523 }
1524
1525 // If the best raw match had a specified skeleton and that skeleton was requested by the caller,
1526 // then return it too. This generally happens when the caller needs to pass that skeleton
1527 // through to adjustFieldTypes so the latter can do a better job.
1528 if (bestPattern && specifiedSkeletonPtr) {
1529 *specifiedSkeletonPtr = specifiedSkeleton;
1530 }
1531 return bestPattern;
1532 }
1533
1534 UnicodeString
1535 DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
1536 const PtnSkeleton* specifiedSkeleton,
1537 int32_t flags,
1538 UDateTimePatternMatchOptions options) {
1539 UnicodeString newPattern;
1540 fp->set(pattern);
1541 for (int32_t i=0; i < fp->itemNumber; i++) {
1542 UnicodeString field = fp->items[i];
1543 if ( fp->isQuoteLiteral(field) ) {
1544
1545 UnicodeString quoteLiteral;
1546 fp->getQuoteLiteral(quoteLiteral, &i);
1547 newPattern += quoteLiteral;
1548 }
1549 else {
1550 if (fp->isPatternSeparator(field)) {
1551 newPattern+=field;
1552 continue;
1553 }
1554 int32_t canonicalIndex = fp->getCanonicalIndex(field);
1555 if (canonicalIndex < 0) {
1556 newPattern+=field;
1557 continue; // don't adjust
1558 }
1559 const dtTypeElem *row = &dtTypes[canonicalIndex];
1560 int32_t typeValue = row->field;
1561
1562 // handle day periods - with #13183, no longer need special handling here, integrated with normal types
1563
1564 if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) {
1565 field += decimal;
1566 dtMatcher->skeleton.original.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD, field);
1567 } else if (dtMatcher->skeleton.type[typeValue]!=0) {
1568 // Here:
1569 // - "reqField" is the field from the originally requested skeleton, with length
1570 // "reqFieldLen".
1571 // - "field" is the field from the found pattern.
1572 //
1573 // The adjusted field should consist of characters from the originally requested
1574 // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
1575 // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
1576 // of characters from the found pattern.
1577 //
1578 // The length of the adjusted field (adjFieldLen) should match that in the originally
1579 // requested skeleton, except that in the following cases the length of the adjusted field
1580 // should match that in the found pattern (i.e. the length of this pattern field should
1581 // not be adjusted):
1582 // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is
1583 // not set (ticket #7180). Note, we may want to implement a similar change for other
1584 // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for
1585 // field length, but options bits can be used to override this.
1586 // 2. There is a specified skeleton for the found pattern and one of the following is true:
1587 // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
1588 // b) The pattern field is numeric and the skeleton field is not, or vice versa.
1589
1590 UChar reqFieldChar = dtMatcher->skeleton.original.getFieldChar(typeValue);
1591 int32_t reqFieldLen = dtMatcher->skeleton.original.getFieldLength(typeValue);
1592 if (reqFieldChar == CAP_E && reqFieldLen < 3)
1593 reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e
1594 int32_t adjFieldLen = reqFieldLen;
1595 if ( (typeValue==UDATPG_HOUR_FIELD && (options & UDATPG_MATCH_HOUR_FIELD_LENGTH)==0) ||
1596 (typeValue==UDATPG_MINUTE_FIELD && (options & UDATPG_MATCH_MINUTE_FIELD_LENGTH)==0) ||
1597 (typeValue==UDATPG_SECOND_FIELD && (options & UDATPG_MATCH_SECOND_FIELD_LENGTH)==0) ) {
1598 adjFieldLen = field.length();
1599 } else if (specifiedSkeleton) {
1600 int32_t skelFieldLen = specifiedSkeleton->original.getFieldLength(typeValue);
1601 UBool patFieldIsNumeric = (row->type > 0);
1602 UBool skelFieldIsNumeric = (specifiedSkeleton->type[typeValue] > 0);
1603 if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) {
1604 // don't adjust the field length in the found pattern
1605 adjFieldLen = field.length();
1606 }
1607 }
1608 UChar c = (typeValue!= UDATPG_HOUR_FIELD
1609 && typeValue!= UDATPG_MONTH_FIELD
1610 && typeValue!= UDATPG_WEEKDAY_FIELD
1611 && (typeValue!= UDATPG_YEAR_FIELD || reqFieldChar==CAP_Y))
1612 ? reqFieldChar
1613 : field.charAt(0);
1614 if (typeValue == UDATPG_HOUR_FIELD && (flags & kDTPGSkeletonUsesCapJ) != 0) {
1615 c = fDefaultHourFormatChar;
1616 switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
1617 case UADATPG_FORCE_12_HOUR_CYCLE:
1618 if (c == CAP_H || c == LOW_K) {
1619 // Have 24-hour cycle, change to 12-hour cycle.
1620 // Should have better way to pick 'h' or 'K'.
1621 c = LOW_H;
1622 };
1623 break;
1624 case UADATPG_FORCE_24_HOUR_CYCLE:
1625 if (c == LOW_H || c == CAP_K) {
1626 // Have 12-hour cycle, change to 24-hour cycle.
1627 // Should have better way to pick 'H' or 'k'.
1628 c = CAP_H;
1629 };
1630 break;
1631 default:
1632 break;
1633 }
1634 }
1635 field.remove();
1636 for (int32_t j=adjFieldLen; j>0; --j) {
1637 field += c;
1638 }
1639 }
1640 newPattern+=field;
1641 }
1642 }
1643 return newPattern;
1644 }
1645
1646 UnicodeString
1647 DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UErrorCode &status, UDateTimePatternMatchOptions options) {
1648 if (U_FAILURE(status)) {
1649 return UnicodeString();
1650 }
1651 UnicodeString resultPattern, tempPattern;
1652 const UnicodeString* tempPatternPtr;
1653 int32_t lastMissingFieldMask=0;
1654 if (missingFields!=0) {
1655 resultPattern=UnicodeString();
1656 const PtnSkeleton* specifiedSkeleton=nullptr;
1657 tempPatternPtr = getBestRaw(*dtMatcher, missingFields, distanceInfo, status, &specifiedSkeleton);
1658 if (U_FAILURE(status)) {
1659 return UnicodeString();
1660 }
1661 tempPattern = *tempPatternPtr;
1662 resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options);
1663 if ( distanceInfo->missingFieldMask==0 ) {
1664 return resultPattern;
1665 }
1666 while (distanceInfo->missingFieldMask!=0) { // precondition: EVERY single field must work!
1667 if ( lastMissingFieldMask == distanceInfo->missingFieldMask ) {
1668 break; // cannot find the proper missing field
1669 }
1670 if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) &&
1671 ((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) {
1672 resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options);
1673 distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK;
1674 continue;
1675 }
1676 int32_t startingMask = distanceInfo->missingFieldMask;
1677 tempPatternPtr = getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, status, &specifiedSkeleton);
1678 if (U_FAILURE(status)) {
1679 return UnicodeString();
1680 }
1681 tempPattern = *tempPatternPtr;
1682 tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options);
1683 int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask;
1684 int32_t topField=getTopBitNumber(foundMask);
1685
1686 if (appendItemFormats[topField].length() != 0) {
1687 UnicodeString appendName;
1688 getAppendName((UDateTimePatternField)topField, appendName);
1689 const UnicodeString *values[3] = {
1690 &resultPattern,
1691 &tempPattern,
1692 &appendName
1693 };
1694 SimpleFormatter(appendItemFormats[topField], 2, 3, status).
1695 formatAndReplace(values, 3, resultPattern, nullptr, 0, status);
1696 }
1697 lastMissingFieldMask = distanceInfo->missingFieldMask;
1698 }
1699 }
1700 return resultPattern;
1701 }
1702
1703 int32_t
1704 DateTimePatternGenerator::getTopBitNumber(int32_t foundMask) const {
1705 if ( foundMask==0 ) {
1706 return 0;
1707 }
1708 int32_t i=0;
1709 while (foundMask!=0) {
1710 foundMask >>=1;
1711 ++i;
1712 }
1713 if (i-1 >UDATPG_ZONE_FIELD) {
1714 return UDATPG_ZONE_FIELD;
1715 }
1716 else
1717 return i-1;
1718 }
1719
1720 void
1721 DateTimePatternGenerator::setAvailableFormat(const UnicodeString &key, UErrorCode& err)
1722 {
1723 fAvailableFormatKeyHash->puti(key, 1, err);
1724 }
1725
1726 UBool
1727 DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString &key) const {
1728 return (UBool)(fAvailableFormatKeyHash->geti(key) == 1);
1729 }
1730
1731 void
1732 DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) {
1733 if (other == nullptr || U_FAILURE(status)) {
1734 return;
1735 }
1736 if (fAvailableFormatKeyHash != nullptr) {
1737 delete fAvailableFormatKeyHash;
1738 fAvailableFormatKeyHash = nullptr;
1739 }
1740 initHashtable(status);
1741 if(U_FAILURE(status)){
1742 return;
1743 }
1744 int32_t pos = UHASH_FIRST;
1745 const UHashElement* elem = nullptr;
1746 // walk through the hash table and create a deep clone
1747 while((elem = other->nextElement(pos))!= nullptr){
1748 const UHashTok otherKeyTok = elem->key;
1749 UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer;
1750 fAvailableFormatKeyHash->puti(*otherKey, 1, status);
1751 if(U_FAILURE(status)){
1752 return;
1753 }
1754 }
1755 }
1756
1757 StringEnumeration*
1758 DateTimePatternGenerator::getSkeletons(UErrorCode& status) const {
1759 if (U_FAILURE(status)) {
1760 return nullptr;
1761 }
1762 if (U_FAILURE(internalErrorCode)) {
1763 status = internalErrorCode;
1764 return nullptr;
1765 }
1766 LocalPointer<StringEnumeration> skeletonEnumerator(
1767 new DTSkeletonEnumeration(*patternMap, DT_SKELETON, status), status);
1768
1769 return U_SUCCESS(status) ? skeletonEnumerator.orphan() : nullptr;
1770 }
1771
1772 const UnicodeString&
1773 DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString& skeleton) const {
1774 PtnElem *curElem;
1775
1776 if (skeleton.length() ==0) {
1777 return emptyString;
1778 }
1779 curElem = patternMap->getHeader(skeleton.charAt(0));
1780 while ( curElem != nullptr ) {
1781 if ( curElem->skeleton->getSkeleton()==skeleton ) {
1782 return curElem->pattern;
1783 }
1784 curElem = curElem->next.getAlias();
1785 }
1786 return emptyString;
1787 }
1788
1789 StringEnumeration*
1790 DateTimePatternGenerator::getBaseSkeletons(UErrorCode& status) const {
1791 if (U_FAILURE(status)) {
1792 return nullptr;
1793 }
1794 if (U_FAILURE(internalErrorCode)) {
1795 status = internalErrorCode;
1796 return nullptr;
1797 }
1798 LocalPointer<StringEnumeration> baseSkeletonEnumerator(
1799 new DTSkeletonEnumeration(*patternMap, DT_BASESKELETON, status), status);
1800
1801 return U_SUCCESS(status) ? baseSkeletonEnumerator.orphan() : nullptr;
1802 }
1803
1804 StringEnumeration*
1805 DateTimePatternGenerator::getRedundants(UErrorCode& status) {
1806 if (U_FAILURE(status)) { return nullptr; }
1807 if (U_FAILURE(internalErrorCode)) {
1808 status = internalErrorCode;
1809 return nullptr;
1810 }
1811 LocalPointer<StringEnumeration> output(new DTRedundantEnumeration(), status);
1812 if (U_FAILURE(status)) { return nullptr; }
1813 const UnicodeString *pattern;
1814 PatternMapIterator it(status);
1815 if (U_FAILURE(status)) { return nullptr; }
1816
1817 for (it.set(*patternMap); it.hasNext(); ) {
1818 DateTimeMatcher current = it.next();
1819 pattern = patternMap->getPatternFromSkeleton(*(it.getSkeleton()));
1820 if ( isCanonicalItem(*pattern) ) {
1821 continue;
1822 }
1823 if ( skipMatcher == nullptr ) {
1824 skipMatcher = new DateTimeMatcher(current);
1825 if (skipMatcher == nullptr) {
1826 status = U_MEMORY_ALLOCATION_ERROR;
1827 return nullptr;
1828 }
1829 }
1830 else {
1831 *skipMatcher = current;
1832 }
1833 UnicodeString trial = getBestPattern(current.getPattern(), status);
1834 if (U_FAILURE(status)) { return nullptr; }
1835 if (trial == *pattern) {
1836 ((DTRedundantEnumeration *)output.getAlias())->add(*pattern, status);
1837 if (U_FAILURE(status)) { return nullptr; }
1838 }
1839 if (current.equals(skipMatcher)) {
1840 continue;
1841 }
1842 }
1843 return output.orphan();
1844 }
1845
1846 UBool
1847 DateTimePatternGenerator::isCanonicalItem(const UnicodeString& item) const {
1848 if ( item.length() != 1 ) {
1849 return FALSE;
1850 }
1851 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
1852 if (item.charAt(0)==Canonical_Items[i]) {
1853 return TRUE;
1854 }
1855 }
1856 return FALSE;
1857 }
1858
1859
1860 DateTimePatternGenerator*
1861 DateTimePatternGenerator::clone() const {
1862 return new DateTimePatternGenerator(*this);
1863 }
1864
1865 PatternMap::PatternMap() {
1866 for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) {
1867 boot[i] = nullptr;
1868 }
1869 isDupAllowed = TRUE;
1870 }
1871
1872 void
1873 PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) {
1874 if (U_FAILURE(status)) {
1875 return;
1876 }
1877 this->isDupAllowed = other.isDupAllowed;
1878 for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) {
1879 PtnElem *curElem, *otherElem, *prevElem=nullptr;
1880 otherElem = other.boot[bootIndex];
1881 while (otherElem != nullptr) {
1882 LocalPointer<PtnElem> newElem(new PtnElem(otherElem->basePattern, otherElem->pattern), status);
1883 if (U_FAILURE(status)) {
1884 return; // out of memory
1885 }
1886 newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(*(otherElem->skeleton)), status);
1887 if (U_FAILURE(status)) {
1888 return; // out of memory
1889 }
1890 newElem->skeletonWasSpecified = otherElem->skeletonWasSpecified;
1891
1892 // Release ownership from the LocalPointer of the PtnElem object.
1893 // The PtnElem will now be owned by either the boot (for the first entry in the linked-list)
1894 // or owned by the previous PtnElem object in the linked-list.
1895 curElem = newElem.orphan();
1896
1897 if (this->boot[bootIndex] == nullptr) {
1898 this->boot[bootIndex] = curElem;
1899 } else {
1900 if (prevElem != nullptr) {
1901 prevElem->next.adoptInstead(curElem);
1902 } else {
1903 UPRV_UNREACHABLE;
1904 }
1905 }
1906 prevElem = curElem;
1907 otherElem = otherElem->next.getAlias();
1908 }
1909
1910 }
1911 }
1912
1913 PtnElem*
1914 PatternMap::getHeader(UChar baseChar) const {
1915 PtnElem* curElem;
1916
1917 if ( (baseChar >= CAP_A) && (baseChar <= CAP_Z) ) {
1918 curElem = boot[baseChar-CAP_A];
1919 }
1920 else {
1921 if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) {
1922 curElem = boot[26+baseChar-LOW_A];
1923 }
1924 else {
1925 return nullptr;
1926 }
1927 }
1928 return curElem;
1929 }
1930
1931 PatternMap::~PatternMap() {
1932 for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) {
1933 if (boot[i] != nullptr ) {
1934 delete boot[i];
1935 boot[i] = nullptr;
1936 }
1937 }
1938 } // PatternMap destructor
1939
1940 void
1941 PatternMap::add(const UnicodeString& basePattern,
1942 const PtnSkeleton& skeleton,
1943 const UnicodeString& value,// mapped pattern value
1944 UBool skeletonWasSpecified,
1945 UErrorCode &status) {
1946 UChar baseChar = basePattern.charAt(0);
1947 PtnElem *curElem, *baseElem;
1948 status = U_ZERO_ERROR;
1949
1950 // the baseChar must be A-Z or a-z
1951 if ((baseChar >= CAP_A) && (baseChar <= CAP_Z)) {
1952 baseElem = boot[baseChar-CAP_A];
1953 }
1954 else {
1955 if ((baseChar >=LOW_A) && (baseChar <= LOW_Z)) {
1956 baseElem = boot[26+baseChar-LOW_A];
1957 }
1958 else {
1959 status = U_ILLEGAL_CHARACTER;
1960 return;
1961 }
1962 }
1963
1964 if (baseElem == nullptr) {
1965 LocalPointer<PtnElem> newElem(new PtnElem(basePattern, value), status);
1966 if (U_FAILURE(status)) {
1967 return; // out of memory
1968 }
1969 newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status);
1970 if (U_FAILURE(status)) {
1971 return; // out of memory
1972 }
1973 newElem->skeletonWasSpecified = skeletonWasSpecified;
1974 if (baseChar >= LOW_A) {
1975 boot[26 + (baseChar - LOW_A)] = newElem.orphan(); // the boot array now owns the PtnElem.
1976 }
1977 else {
1978 boot[baseChar - CAP_A] = newElem.orphan(); // the boot array now owns the PtnElem.
1979 }
1980 }
1981 if ( baseElem != nullptr ) {
1982 curElem = getDuplicateElem(basePattern, skeleton, baseElem);
1983
1984 if (curElem == nullptr) {
1985 // add new element to the list.
1986 curElem = baseElem;
1987 while( curElem -> next != nullptr )
1988 {
1989 curElem = curElem->next.getAlias();
1990 }
1991
1992 LocalPointer<PtnElem> newElem(new PtnElem(basePattern, value), status);
1993 if (U_FAILURE(status)) {
1994 return; // out of memory
1995 }
1996 newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status);
1997 if (U_FAILURE(status)) {
1998 return; // out of memory
1999 }
2000 newElem->skeletonWasSpecified = skeletonWasSpecified;
2001 curElem->next.adoptInstead(newElem.orphan());
2002 curElem = curElem->next.getAlias();
2003 }
2004 else {
2005 // Pattern exists in the list already.
2006 if ( !isDupAllowed ) {
2007 return;
2008 }
2009 // Overwrite the value.
2010 curElem->pattern = value;
2011 // It was a bug that we were not doing the following previously,
2012 // though that bug hid other problems by making things partly work.
2013 curElem->skeletonWasSpecified = skeletonWasSpecified;
2014 }
2015 }
2016 } // PatternMap::add
2017
2018 // Find the pattern from the given basePattern string.
2019 const UnicodeString *
2020 PatternMap::getPatternFromBasePattern(const UnicodeString& basePattern, UBool& skeletonWasSpecified) const { // key to search for
2021 PtnElem *curElem;
2022
2023 if ((curElem=getHeader(basePattern.charAt(0)))==nullptr) {
2024 return nullptr; // no match
2025 }
2026
2027 do {
2028 if ( basePattern.compare(curElem->basePattern)==0 ) {
2029 skeletonWasSpecified = curElem->skeletonWasSpecified;
2030 return &(curElem->pattern);
2031 }
2032 curElem = curElem->next.getAlias();
2033 } while (curElem != nullptr);
2034
2035 return nullptr;
2036 } // PatternMap::getFromBasePattern
2037
2038
2039 // Find the pattern from the given skeleton.
2040 // At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL),
2041 // the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw)
2042 // and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the
2043 // optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL),
2044 // for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily.
2045 const UnicodeString *
2046 PatternMap::getPatternFromSkeleton(const PtnSkeleton& skeleton, const PtnSkeleton** specifiedSkeletonPtr) const { // key to search for
2047 PtnElem *curElem;
2048
2049 if (specifiedSkeletonPtr) {
2050 *specifiedSkeletonPtr = nullptr;
2051 }
2052
2053 // find boot entry
2054 UChar baseChar = skeleton.getFirstChar();
2055 if ((curElem=getHeader(baseChar))==nullptr) {
2056 return nullptr; // no match
2057 }
2058
2059 do {
2060 UBool equal;
2061 if (specifiedSkeletonPtr != nullptr) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
2062 equal = curElem->skeleton->original == skeleton.original;
2063 } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
2064 equal = curElem->skeleton->baseOriginal == skeleton.baseOriginal;
2065 }
2066 if (equal) {
2067 if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) {
2068 *specifiedSkeletonPtr = curElem->skeleton.getAlias();
2069 }
2070 return &(curElem->pattern);
2071 }
2072 curElem = curElem->next.getAlias();
2073 } while (curElem != nullptr);
2074
2075 return nullptr;
2076 }
2077
2078 UBool
2079 PatternMap::equals(const PatternMap& other) const {
2080 if ( this==&other ) {
2081 return TRUE;
2082 }
2083 for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) {
2084 if (boot[bootIndex] == other.boot[bootIndex]) {
2085 continue;
2086 }
2087 if ((boot[bootIndex] == nullptr) || (other.boot[bootIndex] == nullptr)) {
2088 return FALSE;
2089 }
2090 PtnElem *otherElem = other.boot[bootIndex];
2091 PtnElem *myElem = boot[bootIndex];
2092 while ((otherElem != nullptr) || (myElem != nullptr)) {
2093 if ( myElem == otherElem ) {
2094 break;
2095 }
2096 if ((otherElem == nullptr) || (myElem == nullptr)) {
2097 return FALSE;
2098 }
2099 if ( (myElem->basePattern != otherElem->basePattern) ||
2100 (myElem->pattern != otherElem->pattern) ) {
2101 return FALSE;
2102 }
2103 if ((myElem->skeleton.getAlias() != otherElem->skeleton.getAlias()) &&
2104 !myElem->skeleton->equals(*(otherElem->skeleton))) {
2105 return FALSE;
2106 }
2107 myElem = myElem->next.getAlias();
2108 otherElem = otherElem->next.getAlias();
2109 }
2110 }
2111 return TRUE;
2112 }
2113
2114 // find any key existing in the mapping table already.
2115 // return TRUE if there is an existing key, otherwise return FALSE.
2116 PtnElem*
2117 PatternMap::getDuplicateElem(
2118 const UnicodeString &basePattern,
2119 const PtnSkeleton &skeleton,
2120 PtnElem *baseElem) {
2121 PtnElem *curElem;
2122
2123 if ( baseElem == nullptr ) {
2124 return nullptr;
2125 }
2126 else {
2127 curElem = baseElem;
2128 }
2129 do {
2130 if ( basePattern.compare(curElem->basePattern)==0 ) {
2131 UBool isEqual = TRUE;
2132 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
2133 if (curElem->skeleton->type[i] != skeleton.type[i] ) {
2134 isEqual = FALSE;
2135 break;
2136 }
2137 }
2138 if (isEqual) {
2139 return curElem;
2140 }
2141 }
2142 curElem = curElem->next.getAlias();
2143 } while( curElem != nullptr );
2144
2145 // end of the list
2146 return nullptr;
2147
2148 } // PatternMap::getDuplicateElem
2149
2150 DateTimeMatcher::DateTimeMatcher(void) {
2151 }
2152
2153 DateTimeMatcher::~DateTimeMatcher() {}
2154
2155 DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) {
2156 copyFrom(other.skeleton);
2157 }
2158
2159
2160 void
2161 DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp) {
2162 PtnSkeleton localSkeleton;
2163 return set(pattern, fp, localSkeleton);
2164 }
2165
2166 void
2167 DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) {
2168 int32_t i;
2169 for (i=0; i<UDATPG_FIELD_COUNT; ++i) {
2170 skeletonResult.type[i] = NONE;
2171 }
2172 skeletonResult.original.clear();
2173 skeletonResult.baseOriginal.clear();
2174 skeletonResult.addedDefaultDayPeriod = FALSE;
2175
2176 fp->set(pattern);
2177 for (i=0; i < fp->itemNumber; i++) {
2178 const UnicodeString& value = fp->items[i];
2179 // don't skip 'a' anymore, dayPeriod handled specially below
2180
2181 if ( fp->isQuoteLiteral(value) ) {
2182 UnicodeString quoteLiteral;
2183 fp->getQuoteLiteral(quoteLiteral, &i);
2184 continue;
2185 }
2186 int32_t canonicalIndex = fp->getCanonicalIndex(value);
2187 if (canonicalIndex < 0) {
2188 continue;
2189 }
2190 const dtTypeElem *row = &dtTypes[canonicalIndex];
2191 int32_t field = row->field;
2192 skeletonResult.original.populate(field, value);
2193 UChar repeatChar = row->patternChar;
2194 int32_t repeatCount = row->minLen;
2195 skeletonResult.baseOriginal.populate(field, repeatChar, repeatCount);
2196 int16_t subField = row->type;
2197 if (row->type > 0) {
2198 U_ASSERT(value.length() < INT16_MAX);
2199 subField += static_cast<int16_t>(value.length());
2200 }
2201 skeletonResult.type[field] = subField;
2202 }
2203 // #13183, handle special behavior for day period characters (a, b, B)
2204 if (!skeletonResult.original.isFieldEmpty(UDATPG_HOUR_FIELD)) {
2205 if (skeletonResult.original.getFieldChar(UDATPG_HOUR_FIELD)==LOW_H || skeletonResult.original.getFieldChar(UDATPG_HOUR_FIELD)==CAP_K) {
2206 // We have a skeleton with 12-hour-cycle format
2207 if (skeletonResult.original.isFieldEmpty(UDATPG_DAYPERIOD_FIELD)) {
2208 // But we do not have a day period in the skeleton; add the default DAYPERIOD (currently "a")
2209 for (i = 0; dtTypes[i].patternChar != 0; i++) {
2210 if ( dtTypes[i].field == UDATPG_DAYPERIOD_FIELD ) {
2211 // first entry for UDATPG_DAYPERIOD_FIELD
2212 skeletonResult.original.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen);
2213 skeletonResult.baseOriginal.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen);
2214 skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = dtTypes[i].type;
2215 skeletonResult.addedDefaultDayPeriod = TRUE;
2216 break;
2217 }
2218 }
2219 }
2220 } else {
2221 // Skeleton has 24-hour-cycle hour format and has dayPeriod, delete dayPeriod (i.e. ignore it)
2222 skeletonResult.original.clearField(UDATPG_DAYPERIOD_FIELD);
2223 skeletonResult.baseOriginal.clearField(UDATPG_DAYPERIOD_FIELD);
2224 skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = NONE;
2225 }
2226 }
2227 copyFrom(skeletonResult);
2228 }
2229
2230 void
2231 DateTimeMatcher::getBasePattern(UnicodeString &result ) {
2232 result.remove(); // Reset the result first.
2233 skeleton.baseOriginal.appendTo(result);
2234 }
2235
2236 UnicodeString
2237 DateTimeMatcher::getPattern() {
2238 UnicodeString result;
2239 return skeleton.original.appendTo(result);
2240 }
2241
2242 int32_t
2243 DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask, DistanceInfo& distanceInfo) const {
2244 int32_t result = 0;
2245 distanceInfo.clear();
2246 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
2247 int32_t myType = (includeMask&(1<<i))==0 ? 0 : skeleton.type[i];
2248 int32_t otherType = other.skeleton.type[i];
2249 if (myType==otherType) {
2250 continue;
2251 }
2252 if (myType==0) {// and other is not
2253 result += EXTRA_FIELD;
2254 distanceInfo.addExtra(i);
2255 }
2256 else {
2257 if (otherType==0) {
2258 result += MISSING_FIELD;
2259 distanceInfo.addMissing(i);
2260 }
2261 else {
2262 result += abs(myType - otherType);
2263 }
2264 }
2265
2266 }
2267 return result;
2268 }
2269
2270 void
2271 DateTimeMatcher::copyFrom(const PtnSkeleton& newSkeleton) {
2272 skeleton.copyFrom(newSkeleton);
2273 }
2274
2275 void
2276 DateTimeMatcher::copyFrom() {
2277 // same as clear
2278 skeleton.clear();
2279 }
2280
2281 UBool
2282 DateTimeMatcher::equals(const DateTimeMatcher* other) const {
2283 if (other==nullptr) { return FALSE; }
2284 return skeleton.original == other->skeleton.original;
2285 }
2286
2287 int32_t
2288 DateTimeMatcher::getFieldMask() const {
2289 int32_t result = 0;
2290
2291 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
2292 if (skeleton.type[i]!=0) {
2293 result |= (1<<i);
2294 }
2295 }
2296 return result;
2297 }
2298
2299 PtnSkeleton*
2300 DateTimeMatcher::getSkeletonPtr() {
2301 return &skeleton;
2302 }
2303
2304 FormatParser::FormatParser () {
2305 status = START;
2306 itemNumber = 0;
2307 }
2308
2309
2310 FormatParser::~FormatParser () {
2311 }
2312
2313
2314 // Find the next token with the starting position and length
2315 // Note: the startPos may
2316 FormatParser::TokenStatus
2317 FormatParser::setTokens(const UnicodeString& pattern, int32_t startPos, int32_t *len) {
2318 int32_t curLoc = startPos;
2319 if ( curLoc >= pattern.length()) {
2320 return DONE;
2321 }
2322 // check the current char is between A-Z or a-z
2323 do {
2324 UChar c=pattern.charAt(curLoc);
2325 if ( (c>=CAP_A && c<=CAP_Z) || (c>=LOW_A && c<=LOW_Z) ) {
2326 curLoc++;
2327 }
2328 else {
2329 startPos = curLoc;
2330 *len=1;
2331 return ADD_TOKEN;
2332 }
2333
2334 if ( pattern.charAt(curLoc)!= pattern.charAt(startPos) ) {
2335 break; // not the same token
2336 }
2337 } while(curLoc <= pattern.length());
2338 *len = curLoc-startPos;
2339 return ADD_TOKEN;
2340 }
2341
2342 void
2343 FormatParser::set(const UnicodeString& pattern) {
2344 int32_t startPos = 0;
2345 TokenStatus result = START;
2346 int32_t len = 0;
2347 itemNumber = 0;
2348
2349 do {
2350 result = setTokens( pattern, startPos, &len );
2351 if ( result == ADD_TOKEN )
2352 {
2353 items[itemNumber++] = UnicodeString(pattern, startPos, len );
2354 startPos += len;
2355 }
2356 else {
2357 break;
2358 }
2359 } while (result==ADD_TOKEN && itemNumber < MAX_DT_TOKEN);
2360 }
2361
2362 int32_t
2363 FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) {
2364 int32_t len = s.length();
2365 if (len == 0) {
2366 return -1;
2367 }
2368 UChar ch = s.charAt(0);
2369
2370 // Verify that all are the same character.
2371 for (int32_t l = 1; l < len; l++) {
2372 if (ch != s.charAt(l)) {
2373 return -1;
2374 }
2375 }
2376 int32_t i = 0;
2377 int32_t bestRow = -1;
2378 while (dtTypes[i].patternChar != 0x0000) {
2379 if ( dtTypes[i].patternChar != ch ) {
2380 ++i;
2381 continue;
2382 }
2383 bestRow = i;
2384 if (dtTypes[i].patternChar != dtTypes[i+1].patternChar) {
2385 return i;
2386 }
2387 if (dtTypes[i+1].minLen <= len) {
2388 ++i;
2389 continue;
2390 }
2391 return i;
2392 }
2393 return strict ? -1 : bestRow;
2394 }
2395
2396 UBool
2397 FormatParser::isQuoteLiteral(const UnicodeString& s) {
2398 return (UBool)(s.charAt(0) == SINGLE_QUOTE);
2399 }
2400
2401 // This function assumes the current itemIndex points to the quote literal.
2402 // Please call isQuoteLiteral prior to this function.
2403 void
2404 FormatParser::getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex) {
2405 int32_t i = *itemIndex;
2406
2407 quote.remove();
2408 if (items[i].charAt(0)==SINGLE_QUOTE) {
2409 quote += items[i];
2410 ++i;
2411 }
2412 while ( i < itemNumber ) {
2413 if ( items[i].charAt(0)==SINGLE_QUOTE ) {
2414 if ( (i+1<itemNumber) && (items[i+1].charAt(0)==SINGLE_QUOTE)) {
2415 // two single quotes e.g. 'o''clock'
2416 quote += items[i++];
2417 quote += items[i++];
2418 continue;
2419 }
2420 else {
2421 quote += items[i];
2422 break;
2423 }
2424 }
2425 else {
2426 quote += items[i];
2427 }
2428 ++i;
2429 }
2430 *itemIndex=i;
2431 }
2432
2433 UBool
2434 FormatParser::isPatternSeparator(const UnicodeString& field) const {
2435 for (int32_t i=0; i<field.length(); ++i ) {
2436 UChar c= field.charAt(i);
2437 if ( (c==SINGLE_QUOTE) || (c==BACKSLASH) || (c==SPACE) || (c==COLON) ||
2438 (c==QUOTATION_MARK) || (c==COMMA) || (c==HYPHEN) ||(items[i].charAt(0)==DOT) ) {
2439 continue;
2440 }
2441 else {
2442 return FALSE;
2443 }
2444 }
2445 return TRUE;
2446 }
2447
2448 DistanceInfo::~DistanceInfo() {}
2449
2450 void
2451 DistanceInfo::setTo(const DistanceInfo& other) {
2452 missingFieldMask = other.missingFieldMask;
2453 extraFieldMask= other.extraFieldMask;
2454 }
2455
2456 PatternMapIterator::PatternMapIterator(UErrorCode& status) :
2457 bootIndex(0), nodePtr(nullptr), matcher(nullptr), patternMap(nullptr)
2458 {
2459 if (U_FAILURE(status)) { return; }
2460 matcher.adoptInsteadAndCheckErrorCode(new DateTimeMatcher(), status);
2461 }
2462
2463 PatternMapIterator::~PatternMapIterator() {
2464 }
2465
2466 void
2467 PatternMapIterator::set(PatternMap& newPatternMap) {
2468 this->patternMap=&newPatternMap;
2469 }
2470
2471 PtnSkeleton*
2472 PatternMapIterator::getSkeleton() const {
2473 if ( nodePtr == nullptr ) {
2474 return nullptr;
2475 }
2476 else {
2477 return nodePtr->skeleton.getAlias();
2478 }
2479 }
2480
2481 UBool
2482 PatternMapIterator::hasNext() const {
2483 int32_t headIndex = bootIndex;
2484 PtnElem *curPtr = nodePtr;
2485
2486 if (patternMap==nullptr) {
2487 return FALSE;
2488 }
2489 while ( headIndex < MAX_PATTERN_ENTRIES ) {
2490 if ( curPtr != nullptr ) {
2491 if ( curPtr->next != nullptr ) {
2492 return TRUE;
2493 }
2494 else {
2495 headIndex++;
2496 curPtr=nullptr;
2497 continue;
2498 }
2499 }
2500 else {
2501 if ( patternMap->boot[headIndex] != nullptr ) {
2502 return TRUE;
2503 }
2504 else {
2505 headIndex++;
2506 continue;
2507 }
2508 }
2509 }
2510 return FALSE;
2511 }
2512
2513 DateTimeMatcher&
2514 PatternMapIterator::next() {
2515 while ( bootIndex < MAX_PATTERN_ENTRIES ) {
2516 if ( nodePtr != nullptr ) {
2517 if ( nodePtr->next != nullptr ) {
2518 nodePtr = nodePtr->next.getAlias();
2519 break;
2520 }
2521 else {
2522 bootIndex++;
2523 nodePtr=nullptr;
2524 continue;
2525 }
2526 }
2527 else {
2528 if ( patternMap->boot[bootIndex] != nullptr ) {
2529 nodePtr = patternMap->boot[bootIndex];
2530 break;
2531 }
2532 else {
2533 bootIndex++;
2534 continue;
2535 }
2536 }
2537 }
2538 if (nodePtr!=nullptr) {
2539 matcher->copyFrom(*nodePtr->skeleton);
2540 }
2541 else {
2542 matcher->copyFrom();
2543 }
2544 return *matcher;
2545 }
2546
2547
2548 SkeletonFields::SkeletonFields() {
2549 // Set initial values to zero
2550 clear();
2551 }
2552
2553 void SkeletonFields::clear() {
2554 uprv_memset(chars, 0, sizeof(chars));
2555 uprv_memset(lengths, 0, sizeof(lengths));
2556 }
2557
2558 void SkeletonFields::copyFrom(const SkeletonFields& other) {
2559 uprv_memcpy(chars, other.chars, sizeof(chars));
2560 uprv_memcpy(lengths, other.lengths, sizeof(lengths));
2561 }
2562
2563 void SkeletonFields::clearField(int32_t field) {
2564 chars[field] = 0;
2565 lengths[field] = 0;
2566 }
2567
2568 UChar SkeletonFields::getFieldChar(int32_t field) const {
2569 return chars[field];
2570 }
2571
2572 int32_t SkeletonFields::getFieldLength(int32_t field) const {
2573 return lengths[field];
2574 }
2575
2576 void SkeletonFields::populate(int32_t field, const UnicodeString& value) {
2577 populate(field, value.charAt(0), value.length());
2578 }
2579
2580 void SkeletonFields::populate(int32_t field, UChar ch, int32_t length) {
2581 chars[field] = (int8_t) ch;
2582 lengths[field] = (int8_t) length;
2583 }
2584
2585 UBool SkeletonFields::isFieldEmpty(int32_t field) const {
2586 return lengths[field] == 0;
2587 }
2588
2589 UnicodeString& SkeletonFields::appendTo(UnicodeString& string) const {
2590 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
2591 appendFieldTo(i, string);
2592 }
2593 return string;
2594 }
2595
2596 UnicodeString& SkeletonFields::appendFieldTo(int32_t field, UnicodeString& string) const {
2597 UChar ch(chars[field]);
2598 int32_t length = (int32_t) lengths[field];
2599
2600 for (int32_t i=0; i<length; i++) {
2601 string += ch;
2602 }
2603 return string;
2604 }
2605
2606 UChar SkeletonFields::getFirstChar() const {
2607 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
2608 if (lengths[i] != 0) {
2609 return chars[i];
2610 }
2611 }
2612 return '\0';
2613 }
2614
2615
2616 PtnSkeleton::PtnSkeleton()
2617 : addedDefaultDayPeriod(FALSE) {
2618 }
2619
2620 PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) {
2621 copyFrom(other);
2622 }
2623
2624 void PtnSkeleton::copyFrom(const PtnSkeleton& other) {
2625 uprv_memcpy(type, other.type, sizeof(type));
2626 original.copyFrom(other.original);
2627 baseOriginal.copyFrom(other.baseOriginal);
2628 addedDefaultDayPeriod = other.addedDefaultDayPeriod;
2629 }
2630
2631 void PtnSkeleton::clear() {
2632 uprv_memset(type, 0, sizeof(type));
2633 original.clear();
2634 baseOriginal.clear();
2635 }
2636
2637 UBool
2638 PtnSkeleton::equals(const PtnSkeleton& other) const {
2639 return (original == other.original)
2640 && (baseOriginal == other.baseOriginal)
2641 && (uprv_memcmp(type, other.type, sizeof(type)) == 0);
2642 }
2643
2644 UnicodeString
2645 PtnSkeleton::getSkeleton() const {
2646 UnicodeString result;
2647 result = original.appendTo(result);
2648 int32_t pos;
2649 if (addedDefaultDayPeriod && (pos = result.indexOf(LOW_A)) >= 0) {
2650 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2651 // was not in the provided skeleton, remove it here before returning skeleton.
2652 result.remove(pos, 1);
2653 }
2654 return result;
2655 }
2656
2657 UnicodeString
2658 PtnSkeleton::getBaseSkeleton() const {
2659 UnicodeString result;
2660 result = baseOriginal.appendTo(result);
2661 int32_t pos;
2662 if (addedDefaultDayPeriod && (pos = result.indexOf(LOW_A)) >= 0) {
2663 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2664 // was not in the provided skeleton, remove it here before returning skeleton.
2665 result.remove(pos, 1);
2666 }
2667 return result;
2668 }
2669
2670 UChar
2671 PtnSkeleton::getFirstChar() const {
2672 return baseOriginal.getFirstChar();
2673 }
2674
2675 PtnSkeleton::~PtnSkeleton() {
2676 }
2677
2678 PtnElem::PtnElem(const UnicodeString &basePat, const UnicodeString &pat) :
2679 basePattern(basePat), skeleton(nullptr), pattern(pat), next(nullptr)
2680 {
2681 }
2682
2683 PtnElem::~PtnElem() {
2684 }
2685
2686 DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum type, UErrorCode& status) : fSkeletons(nullptr) {
2687 PtnElem *curElem;
2688 PtnSkeleton *curSkeleton;
2689 UnicodeString s;
2690 int32_t bootIndex;
2691
2692 pos=0;
2693 fSkeletons.adoptInsteadAndCheckErrorCode(new UVector(status), status);
2694 if (U_FAILURE(status)) {
2695 return;
2696 }
2697
2698 for (bootIndex=0; bootIndex<MAX_PATTERN_ENTRIES; ++bootIndex ) {
2699 curElem = patternMap.boot[bootIndex];
2700 while (curElem!=nullptr) {
2701 switch(type) {
2702 case DT_BASESKELETON:
2703 s=curElem->basePattern;
2704 break;
2705 case DT_PATTERN:
2706 s=curElem->pattern;
2707 break;
2708 case DT_SKELETON:
2709 curSkeleton=curElem->skeleton.getAlias();
2710 s=curSkeleton->getSkeleton();
2711 break;
2712 }
2713 if ( !isCanonicalItem(s) ) {
2714 LocalPointer<UnicodeString> newElem(new UnicodeString(s), status);
2715 if (U_FAILURE(status)) {
2716 return;
2717 }
2718 fSkeletons->addElement(newElem.getAlias(), status);
2719 if (U_FAILURE(status)) {
2720 fSkeletons.adoptInstead(nullptr);
2721 return;
2722 }
2723 newElem.orphan(); // fSkeletons vector now owns the UnicodeString.
2724 }
2725 curElem = curElem->next.getAlias();
2726 }
2727 }
2728 if ((bootIndex==MAX_PATTERN_ENTRIES) && (curElem!=nullptr) ) {
2729 status = U_BUFFER_OVERFLOW_ERROR;
2730 }
2731 }
2732
2733 const UnicodeString*
2734 DTSkeletonEnumeration::snext(UErrorCode& status) {
2735 if (U_SUCCESS(status) && fSkeletons.isValid() && pos < fSkeletons->size()) {
2736 return (const UnicodeString*)fSkeletons->elementAt(pos++);
2737 }
2738 return nullptr;
2739 }
2740
2741 void
2742 DTSkeletonEnumeration::reset(UErrorCode& /*status*/) {
2743 pos=0;
2744 }
2745
2746 int32_t
2747 DTSkeletonEnumeration::count(UErrorCode& /*status*/) const {
2748 return (fSkeletons.isNull()) ? 0 : fSkeletons->size();
2749 }
2750
2751 UBool
2752 DTSkeletonEnumeration::isCanonicalItem(const UnicodeString& item) {
2753 if ( item.length() != 1 ) {
2754 return FALSE;
2755 }
2756 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
2757 if (item.charAt(0)==Canonical_Items[i]) {
2758 return TRUE;
2759 }
2760 }
2761 return FALSE;
2762 }
2763
2764 DTSkeletonEnumeration::~DTSkeletonEnumeration() {
2765 UnicodeString *s;
2766 if (fSkeletons.isValid()) {
2767 for (int32_t i = 0; i < fSkeletons->size(); ++i) {
2768 if ((s = (UnicodeString *)fSkeletons->elementAt(i)) != nullptr) {
2769 delete s;
2770 }
2771 }
2772 }
2773 }
2774
2775 DTRedundantEnumeration::DTRedundantEnumeration() : pos(0), fPatterns(nullptr) {
2776 }
2777
2778 void
2779 DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) {
2780 if (U_FAILURE(status)) { return; }
2781 if (fPatterns.isNull()) {
2782 fPatterns.adoptInsteadAndCheckErrorCode(new UVector(status), status);
2783 if (U_FAILURE(status)) {
2784 return;
2785 }
2786 }
2787 LocalPointer<UnicodeString> newElem(new UnicodeString(pattern), status);
2788 if (U_FAILURE(status)) {
2789 return;
2790 }
2791 fPatterns->addElement(newElem.getAlias(), status);
2792 if (U_FAILURE(status)) {
2793 fPatterns.adoptInstead(nullptr);
2794 return;
2795 }
2796 newElem.orphan(); // fPatterns now owns the string.
2797 }
2798
2799 const UnicodeString*
2800 DTRedundantEnumeration::snext(UErrorCode& status) {
2801 if (U_SUCCESS(status) && fPatterns.isValid() && pos < fPatterns->size()) {
2802 return (const UnicodeString*)fPatterns->elementAt(pos++);
2803 }
2804 return nullptr;
2805 }
2806
2807 void
2808 DTRedundantEnumeration::reset(UErrorCode& /*status*/) {
2809 pos=0;
2810 }
2811
2812 int32_t
2813 DTRedundantEnumeration::count(UErrorCode& /*status*/) const {
2814 return (fPatterns.isNull()) ? 0 : fPatterns->size();
2815 }
2816
2817 UBool
2818 DTRedundantEnumeration::isCanonicalItem(const UnicodeString& item) const {
2819 if ( item.length() != 1 ) {
2820 return FALSE;
2821 }
2822 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
2823 if (item.charAt(0)==Canonical_Items[i]) {
2824 return TRUE;
2825 }
2826 }
2827 return FALSE;
2828 }
2829
2830 DTRedundantEnumeration::~DTRedundantEnumeration() {
2831 UnicodeString *s;
2832 if (fPatterns.isValid()) {
2833 for (int32_t i = 0; i < fPatterns->size(); ++i) {
2834 if ((s = (UnicodeString *)fPatterns->elementAt(i)) != nullptr) {
2835 delete s;
2836 }
2837 }
2838 }
2839 }
2840
2841 /**
2842 * This is a utility function used by the various date formatting classes to determine whether a particular pattern string will produce an all-numeric date.
2843 * It does this by examining the pattern string. If the range between the first d, M, or y and the last d, M, or y in the pattern contains nothing but d, M, y,
2844 * punctuation, whitespace, and Unicode right-to-left marks, and it doesn't contain more than two Ms in a row, it's considered to have a "numeric core"--
2845 * that is, the part of the pattern that generates a date (minus fields like the day of the week and the era) produces an all-numeric date.
2846 */
2847 extern UBool datePatternHasNumericCore(const UnicodeString& datePattern) {
2848 StringCharacterIterator it = StringCharacterIterator(datePattern);
2849 int32_t coreStart = -1;
2850 int32_t coreEnd = -1;
2851 int32_t firstLetterAfterCoreStart = -1;
2852 int32_t numMs = 0;
2853 UBool sawD = FALSE, sawY = FALSE;
2854 for (UChar c = it.first(); it.hasNext(); c = it.next()) {
2855 switch (c) {
2856 case u'y': case u'Y': case u'r': case u'u':
2857 case u'M': case u'L': case u'd':
2858 if (coreStart == -1) {
2859 coreStart = it.getIndex();
2860 }
2861 coreEnd = it.getIndex();
2862 switch (c) {
2863 case u'y': case u'Y': case u'r': case u'u':
2864 sawY = TRUE;
2865 break;
2866 case u'M': case u'L':
2867 // if the pattern contains more than 2 M's, the month is a word, not a number, which means
2868 // we don't have a numeric core
2869 ++numMs;
2870 if (numMs > 2) {
2871 return FALSE;
2872 }
2873 break;
2874 case 'd':
2875 sawD = TRUE;
2876 break;
2877 default:
2878 break;
2879 }
2880 break;
2881 default:
2882 if (u_isalpha(c)) {
2883 if (coreStart != -1 && firstLetterAfterCoreStart == -1) {
2884 firstLetterAfterCoreStart = it.getIndex();
2885 }
2886 } else if (!u_isspace(c) && !u_ispunct(c) && c != u'\u200f') {
2887 // the numeric core must contain nothing but d, M, y, whitespace, punctuation, and the Unicode right-to-left mark
2888 return FALSE;
2889 }
2890 break;
2891 }
2892 }
2893
2894 // if we didn't find d, M, or y in the pattern, return FALSE
2895 if (coreStart < 0 || coreEnd < 0) {
2896 return FALSE;
2897 }
2898
2899 // if there's quoted literal text anywhere in the pattern, whether in the "core" or not, treat it as though
2900 // we don't have a numeric core
2901 if (datePattern.indexOf(u'\'') != -1) {
2902 return FALSE;
2903 }
2904
2905 // if we found a letter other than d, M, or y between the first d, M, or y and the last one,
2906 // we don't have a numeric core
2907 if (firstLetterAfterCoreStart != -1 && firstLetterAfterCoreStart < coreEnd) {
2908 return FALSE;
2909 }
2910
2911 // if the format contains only one numeric field (out of d, M, or y), we don't count it as a numeric core
2912 if (((numMs > 0) ? 1 : 0) + (sawY ? 1 : 0) + (sawD ? 1 : 0) <= 1) {
2913 return FALSE;
2914 }
2915
2916 // if we get to here, we have a numeric core
2917 return TRUE;
2918 }
2919
2920 U_NAMESPACE_END
2921
2922
2923 #endif /* #if !UCONFIG_NO_FORMATTING */
2924
2925 //eof