]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/dtptngen.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / dtptngen.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
46f4442e
A
3/*
4*******************************************************************************
2ca993e8 5* Copyright (C) 2007-2016, International Business Machines Corporation and
46f4442e
A
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"
3d1f044b 21#include "unicode/localpointer.h"
340931cb 22#include "unicode/schriter.h"
2ca993e8 23#include "unicode/simpleformatter.h"
46f4442e
A
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"
340931cb 32#include "unicode/region.h"
46f4442e 33#include "cpputils.h"
46f4442e 34#include "mutex.h"
2ca993e8 35#include "umutex.h"
46f4442e
A
36#include "cmemory.h"
37#include "cstring.h"
38#include "locbased.h"
46f4442e 39#include "hash.h"
2ca993e8 40#include "uhash.h"
46f4442e
A
41#include "uresimp.h"
42#include "dtptngen_impl.h"
2ca993e8
A
43#include "ucln_in.h"
44#include "charstr.h"
f3c0d7a5 45#include "uassert.h"
46f4442e
A
46
47#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
48/**
729e4ab9 49 * If we are on EBCDIC, use an iterator which will
46f4442e
A
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
61struct UResAEntry {
62 UChar *key;
63 UResourceBundle *item;
64};
65
66struct 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
4388f060
A
75U_CDECL_BEGIN
76
729e4ab9 77static int32_t U_CALLCONV
46f4442e
A
78ures_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
4388f060 84U_CDECL_END
46f4442e
A
85
86static 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)
3d1f044b 94 aiter->entries = nullptr;
46f4442e
A
95#else
96 aiter->entries = (UResAEntry*)uprv_malloc(sizeof(UResAEntry)*aiter->num);
97 for(int i=0;i<aiter->num;i++) {
3d1f044b 98 aiter->entries[i].item = ures_getByIndex(aiter->bund, i, nullptr, status);
46f4442e
A
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 }
3d1f044b 104 uprv_sortArray(aiter->entries, aiter->num, sizeof(UResAEntry), ures_a_codepointSort, nullptr, TRUE, status);
46f4442e
A
105#endif
106}
107
108static 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
117static 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
3d1f044b 121 if(U_FAILURE(*err)) return nullptr;
46f4442e
A
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
134U_NAMESPACE_BEGIN
135
46f4442e
A
136// *****************************************************************************
137// class DateTimePatternGenerator
138// *****************************************************************************
139static const UChar Canonical_Items[] = {
0f5d89e8
A
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
46f4442e
A
143 CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0
144};
145
146static const dtTypeElem dtTypes[] = {
147 // patternChar, field, type, minLen, weight
148 {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,},
0f5d89e8
A
149 {CAP_G, UDATPG_ERA_FIELD, DT_LONG, 4, 0},
150 {CAP_G, UDATPG_ERA_FIELD, DT_NARROW, 5, 0},
151
46f4442e
A
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},
57a6839d 155 {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20},
4388f060
A
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},
0f5d89e8 159
46f4442e
A
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},
0f5d89e8 163 {CAP_Q, UDATPG_QUARTER_FIELD, DT_NARROW, 5, 0},
729e4ab9 164 {LOW_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC + DT_DELTA, 1, 2},
0f5d89e8
A
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
46f4442e
A
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},
4388f060 177 {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1},
0f5d89e8 178
46f4442e 179 {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2},
0f5d89e8
A
180
181 {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC, 1, 0},
182
46f4442e
A
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},
0f5d89e8 186 {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER, 6, 0},
46f4442e
A
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},
0f5d89e8 191 {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORTER - 2*DT_DELTA, 6, 0},
729e4ab9
A
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},
0f5d89e8
A
196 {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER - DT_DELTA, 6, 0},
197
46f4442e 198 {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2},
0f5d89e8
A
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
46f4442e 216 {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour
57a6839d 217 {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour
46f4442e 218 {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour
57a6839d 219 {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour
0f5d89e8
A
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
46f4442e 231 {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2},
0f5d89e8 232
46f4442e 233 {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2},
0f5d89e8
A
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
46f4442e
A
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},
57a6839d 242 {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3},
46f4442e 243 {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
57a6839d
A
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},
0f5d89e8
A
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},
57a6839d
A
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},
0f5d89e8 257
729e4ab9 258 {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[]
46f4442e
A
259 };
260
261static const char* const CLDR_FIELD_APPEND[] = {
0f5d89e8
A
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
46f4442e
A
264 "Hour", "Minute", "Second", "*", "Timezone"
265};
266
0f5d89e8
A
267static 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
46f4442e 270 "hour", "minute", "second", "*", "zone"
729e4ab9 271};
46f4442e 272
0f5d89e8
A
273static 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.
278static const int32_t UDATPG_WIDTH_COUNT = UDATPG_NARROW + 1;
279static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE;
280static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width)
281
46f4442e
A
282// For appendItems
283static 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
51004dcb 286//static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
46f4442e
A
287
288static const char DT_DateTimePatternsTag[]="DateTimePatterns";
289static const char DT_DateTimeCalendarTag[]="calendar";
290static const char DT_DateTimeGregorianTag[]="gregorian";
291static const char DT_DateTimeAppendItemsTag[]="appendItems";
292static const char DT_DateTimeFieldsTag[]="fields";
293static const char DT_DateTimeAvailableFormatsTag[]="availableFormats";
294//static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);
295
296UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator)
297UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration)
298UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration)
299
300DateTimePatternGenerator* U_EXPORT2
301DateTimePatternGenerator::createInstance(UErrorCode& status) {
302 return createInstance(Locale::getDefault(), status);
303}
304
305DateTimePatternGenerator* U_EXPORT2
3d1f044b 306DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status, UBool skipICUData) {
46f4442e 307 if (U_FAILURE(status)) {
3d1f044b 308 return nullptr;
46f4442e 309 }
2ca993e8 310 LocalPointer<DateTimePatternGenerator> result(
3d1f044b
A
311 new DateTimePatternGenerator(locale, status, skipICUData), status);
312 return U_SUCCESS(status) ? result.orphan() : nullptr;
46f4442e
A
313}
314
315DateTimePatternGenerator* U_EXPORT2
316DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) {
46f4442e 317 if (U_FAILURE(status)) {
3d1f044b 318 return nullptr;
46f4442e 319 }
3d1f044b
A
320 LocalPointer<DateTimePatternGenerator> result(
321 new DateTimePatternGenerator(status), status);
322 return U_SUCCESS(status) ? result.orphan() : nullptr;
46f4442e
A
323}
324
729e4ab9 325DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) :
3d1f044b
A
326 skipMatcher(nullptr),
327 fAvailableFormatKeyHash(nullptr),
328 internalErrorCode(U_ZERO_ERROR)
46f4442e
A
329{
330 fp = new FormatParser();
331 dtMatcher = new DateTimeMatcher();
332 distanceInfo = new DistanceInfo();
333 patternMap = new PatternMap();
3d1f044b
A
334 if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
335 internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR;
46f4442e
A
336 }
337}
338
3d1f044b
A
339DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status, UBool skipICUData) :
340 skipMatcher(nullptr),
341 fAvailableFormatKeyHash(nullptr),
340931cb
A
342 internalErrorCode(U_ZERO_ERROR),
343 pLocale(locale)
46f4442e
A
344{
345 fp = new FormatParser();
346 dtMatcher = new DateTimeMatcher();
347 distanceInfo = new DistanceInfo();
348 patternMap = new PatternMap();
3d1f044b
A
349 if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
350 internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR;
46f4442e
A
351 }
352 else {
3d1f044b 353 initData(locale, status, skipICUData);
46f4442e
A
354 }
355}
356
729e4ab9 357DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator& other) :
46f4442e 358 UObject(),
3d1f044b
A
359 skipMatcher(nullptr),
360 fAvailableFormatKeyHash(nullptr),
361 internalErrorCode(U_ZERO_ERROR)
46f4442e
A
362{
363 fp = new FormatParser();
729e4ab9 364 dtMatcher = new DateTimeMatcher();
46f4442e
A
365 distanceInfo = new DistanceInfo();
366 patternMap = new PatternMap();
3d1f044b
A
367 if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
368 internalErrorCode = U_MEMORY_ALLOCATION_ERROR;
369 }
46f4442e
A
370 *this=other;
371}
372
373DateTimePatternGenerator&
374DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
b331163b
A
375 // reflexive case
376 if (&other == this) {
377 return *this;
378 }
3d1f044b 379 internalErrorCode = other.internalErrorCode;
46f4442e
A
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;
3d1f044b
A
391 if ( other.skipMatcher == nullptr ) {
392 skipMatcher = nullptr;
46f4442e
A
393 }
394 else {
395 skipMatcher = new DateTimeMatcher(*other.skipMatcher);
3d1f044b
A
396 if (skipMatcher == nullptr)
397 {
398 internalErrorCode = U_MEMORY_ALLOCATION_ERROR;
399 return *this;
400 }
46f4442e
A
401 }
402 for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) {
403 appendItemFormats[i] = other.appendItemFormats[i];
0f5d89e8
A
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 }
46f4442e 409 }
3d1f044b
A
410 patternMap->copyFrom(*other.patternMap, internalErrorCode);
411 copyHashtable(other.fAvailableFormatKeyHash, internalErrorCode);
46f4442e
A
412 return *this;
413}
414
415
416UBool
417DateTimePatternGenerator::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 ) {
0f5d89e8
A
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 }
46f4442e
A
432 }
433 return TRUE;
434 }
435 else {
436 return FALSE;
437 }
438}
439
440UBool
441DateTimePatternGenerator::operator!=(const DateTimePatternGenerator& other) const {
442 return !operator==(other);
443}
444
445DateTimePatternGenerator::~DateTimePatternGenerator() {
3d1f044b 446 if (fAvailableFormatKeyHash!=nullptr) {
46f4442e
A
447 delete fAvailableFormatKeyHash;
448 }
729e4ab9 449
3d1f044b
A
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;
46f4442e
A
455}
456
2ca993e8
A
457namespace {
458
459UInitOnce initOnce = U_INITONCE_INITIALIZER;
3d1f044b 460UHashtable *localeToAllowedHourFormatsMap = nullptr;
2ca993e8
A
461
462// Value deleter for hashmap.
f3c0d7a5 463U_CFUNC void U_CALLCONV deleteAllowedHourFormats(void *ptr) {
2ca993e8
A
464 uprv_free(ptr);
465}
466
467// Close hashmap at cleanup.
f3c0d7a5 468U_CFUNC UBool U_CALLCONV allowedHourFormatsCleanup() {
2ca993e8
A
469 uhash_close(localeToAllowedHourFormatsMap);
470 return TRUE;
471}
472
473enum AllowedHourFormat{
474 ALLOWED_HOUR_FORMAT_UNKNOWN = -1,
475 ALLOWED_HOUR_FORMAT_h,
476 ALLOWED_HOUR_FORMAT_H,
3d1f044b
A
477 ALLOWED_HOUR_FORMAT_K, // Added ICU-20383, used by JP
478 ALLOWED_HOUR_FORMAT_k, // Added ICU-20383, not currently used
2ca993e8 479 ALLOWED_HOUR_FORMAT_hb,
2ca993e8 480 ALLOWED_HOUR_FORMAT_hB,
3d1f044b
A
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,
2ca993e8
A
485 ALLOWED_HOUR_FORMAT_HB
486};
487
488} // namespace
489
46f4442e 490void
3d1f044b 491DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status, UBool skipICUData) {
46f4442e 492 //const char *baseLangName = locale.getBaseName(); // unused
729e4ab9 493
3d1f044b
A
494 skipMatcher = nullptr;
495 fAvailableFormatKeyHash=nullptr;
f3c0d7a5 496 addCanonicalItems(status);
3d1f044b
A
497 if (!skipICUData) {
498 addICUPatterns(locale, status); // skip to prevent circular dependency when called from SimpleDateFormat::construct
499 }
729e4ab9 500 addCLDRData(locale, status);
46f4442e
A
501 setDateTimeFromCalendar(locale, status);
502 setDecimalSymbols(locale, status);
2ca993e8
A
503 umtx_initOnce(initOnce, loadAllowedHourFormatsData, status);
504 getAllowedHourFormats(locale, status);
3d1f044b
A
505 // If any of the above methods failed then the object is in an invalid state.
506 internalErrorCode = status;
46f4442e
A
507} // DateTimePatternGenerator::initData
508
2ca993e8
A
509namespace {
510
f3c0d7a5 511struct AllowedHourFormatsSink : public ResourceSink {
2ca993e8 512 // Initialize sub-sinks.
f3c0d7a5 513 AllowedHourFormatsSink() {}
2ca993e8
A
514 virtual ~AllowedHourFormatsSink();
515
f3c0d7a5
A
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; }
3d1f044b
A
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;
f3c0d7a5 530 for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) {
3d1f044b 531 if (uprv_strcmp(key, "allowed") == 0) {
f3c0d7a5 532 if (value.getType() == URES_STRING) {
3d1f044b
A
533 length = 2; // 1 preferred to add later, 1 allowed to add now
534 if (list.allocateInsteadAndReset(length + 1) == nullptr) {
f3c0d7a5
A
535 errorCode = U_MEMORY_ALLOCATION_ERROR;
536 return;
537 }
3d1f044b 538 list[1] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
f3c0d7a5
A
539 }
540 else {
541 ResourceArray allowedFormats = value.getArray(errorCode);
3d1f044b
A
542 length = allowedFormats.getSize() + 1; // 1 preferred, getSize allowed
543 if (list.allocateInsteadAndReset(length + 1) == nullptr) {
f3c0d7a5
A
544 errorCode = U_MEMORY_ALLOCATION_ERROR;
545 return;
546 }
3d1f044b
A
547 for (int32_t k = 1; k < length; ++k) {
548 allowedFormats.getValue(k-1, value);
f3c0d7a5
A
549 list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
550 }
551 }
3d1f044b
A
552 } else if (uprv_strcmp(key, "preferred") == 0) {
553 preferredFormat = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
2ca993e8
A
554 }
555 }
3d1f044b
A
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; }
2ca993e8 573 }
f3c0d7a5 574 }
2ca993e8 575
f3c0d7a5 576 AllowedHourFormat getHourFormatFromUnicodeString(const UnicodeString &s) {
2ca993e8
A
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; }
3d1f044b
A
580 if (s[0] == CAP_K) { return ALLOWED_HOUR_FORMAT_K; }
581 if (s[0] == LOW_K) { return ALLOWED_HOUR_FORMAT_k; }
2ca993e8
A
582 } else if (s.length() == 2) {
583 if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; }
2ca993e8 584 if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; }
3d1f044b
A
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; }
2ca993e8
A
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
597AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
2ca993e8 598
f3c0d7a5 599U_CFUNC void U_CALLCONV DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) {
2ca993e8
A
600 if (U_FAILURE(status)) { return; }
601 localeToAllowedHourFormatsMap = uhash_open(
3d1f044b
A
602 uhash_hashChars, uhash_compareChars, nullptr, &status);
603 if (U_FAILURE(status)) { return; }
604
2ca993e8 605 uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats);
3d1f044b
A
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; }
2ca993e8
A
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.
3d1f044b 618 ures_getAllItemsWithFallback(rb.getAlias(), "timeData", sink, status);
2ca993e8
A
619}
620
340931cb
A
621static 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
2ca993e8
A
636void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
637 if (U_FAILURE(status)) { return; }
340931cb
A
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();
3d1f044b 654 }
2ca993e8 655 }
340931cb
A
656 if (*language == '\0') {
657 // Unexpected, but fail gracefully
658 language = "und";
659 }
660 if (*country == '\0') {
661 country = "001";
662 }
2ca993e8 663
340931cb 664 int32_t* allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);
2ca993e8 665
340931cb 666 // Check if the region has an alias
3d1f044b 667 if (allowedFormats == nullptr) {
340931cb
A
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 }
2ca993e8
A
674 }
675
3d1f044b
A
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 }
2ca993e8 686 for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) {
3d1f044b
A
687 fAllowedHourFormats[i] = allowedFormats[i + 1];
688 if (fAllowedHourFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) {
2ca993e8
A
689 break;
690 }
691 }
692 } else { // Lookup failed, twice
3d1f044b 693 fDefaultHourFormatChar = CAP_H;
2ca993e8
A
694 fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H;
695 fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN;
696 }
697}
698
46f4442e
A
699UnicodeString
700DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode&
701/*status*/) {
3d1f044b 702 FormatParser fp2;
2ca993e8
A
703 DateTimeMatcher matcher;
704 PtnSkeleton localSkeleton;
3d1f044b 705 matcher.set(pattern, &fp2, localSkeleton);
2ca993e8
A
706 return localSkeleton.getSkeleton();
707}
708
709UnicodeString
710DateTimePatternGenerator::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();
46f4442e
A
717}
718
719UnicodeString
720DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) {
3d1f044b 721 FormatParser fp2;
2ca993e8
A
722 DateTimeMatcher matcher;
723 PtnSkeleton localSkeleton;
3d1f044b 724 matcher.set(pattern, &fp2, localSkeleton);
2ca993e8
A
725 return localSkeleton.getBaseSkeleton();
726}
727
728UnicodeString
729DateTimePatternGenerator::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();
46f4442e
A
736}
737
738void
739DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) {
f3c0d7a5 740 if (U_FAILURE(status)) { return; }
46f4442e
A
741 UnicodeString dfPattern;
742 UnicodeString conflictingString;
46f4442e
A
743 DateFormat* df;
744
46f4442e
A
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);
729e4ab9 749 SimpleDateFormat* sdf;
3d1f044b 750 if (df != nullptr && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != nullptr) {
f3c0d7a5
A
751 sdf->toPattern(dfPattern);
752 addPattern(dfPattern, FALSE, conflictingString, status);
46f4442e
A
753 }
754 // TODO Maybe we should return an error when the date format isn't simple.
755 delete df;
f3c0d7a5 756 if (U_FAILURE(status)) { return; }
46f4442e
A
757
758 df = DateFormat::createTimeInstance(style, locale);
3d1f044b 759 if (df != nullptr && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != nullptr) {
f3c0d7a5
A
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);
46f4442e
A
767 }
768 }
769 // TODO Maybe we should return an error when the date format isn't simple.
770 delete df;
f3c0d7a5 771 if (U_FAILURE(status)) { return; }
46f4442e
A
772 }
773}
774
775void
776DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status) {
46f4442e 777 UnicodeString conflictingString;
729e4ab9 778
46f4442e
A
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;
4388f060 807 addPattern(mmss, FALSE, conflictingString, status);
46f4442e
A
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
46f4442e 823void
f3c0d7a5
A
824DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString& destination, UErrorCode& err) {
825 destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default
46f4442e 826 if ( U_SUCCESS(err) ) {
340931cb
A
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 }
46f4442e 840 }
f3c0d7a5 841}
46f4442e 842
f3c0d7a5
A
843void
844DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern,
845 UErrorCode& status) {
3d1f044b
A
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.
729e4ab9 850
f3c0d7a5
A
851 // HACK for hh:ss
852 hackTimes(shortTimePattern, status);
853}
854
855struct 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 }
46f4442e
A
874 }
875 }
729e4ab9 876
f3c0d7a5
A
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 }
46f4442e
A
884 }
885 }
f3c0d7a5 886};
46f4442e 887
f3c0d7a5
A
888struct 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) {
0f5d89e8
A
901 UDateTimePGDisplayWidth width;
902 UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width);
f3c0d7a5
A
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);
0f5d89e8
A
909 if (dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) {
910 dtpg.setFieldDisplayName(field,width,valueStr);
46f4442e 911 }
f3c0d7a5 912 break;
4388f060 913 }
46f4442e 914 }
f3c0d7a5
A
915 }
916
917 void fillInMissing() {
918 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
0f5d89e8 919 UnicodeString& valueStr = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, UDATPG_WIDE);
f3c0d7a5
A
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();
4388f060 933 }
0f5d89e8 934 for (int32_t j = 1; j < UDATPG_WIDTH_COUNT; j++) {
3d1f044b
A
935 UnicodeString& valueStr2 = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)j);
936 if (valueStr2.isEmpty()) {
937 valueStr2 = dtpg.getFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)(j-1));
0f5d89e8
A
938 }
939 }
4388f060 940 }
f3c0d7a5
A
941 }
942};
943
944struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink {
945
946 // Destination for data, modified via setters.
947 DateTimePatternGenerator& dtpg;
340931cb
A
948
949 // UBool flag indicating whether to populate the generator with all patterns or just date patterns with numeric cores
950 UBool onlyDatesWithNumericCores;
f3c0d7a5
A
951
952 // Temporary variable, required for calling addPatternWithSkeleton.
953 UnicodeString conflictingPattern;
954
340931cb 955 AvailableFormatsSink(DateTimePatternGenerator& _dtpg, UBool onlyDatesWithNumericCores) : dtpg(_dtpg), onlyDatesWithNumericCores(onlyDatesWithNumericCores) {}
f3c0d7a5
A
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) {
340931cb 963 UErrorCode valueErr = U_ZERO_ERROR; // if the resource value isn't a string, skip it without returning an error to the caller
f3c0d7a5 964 const UnicodeString formatKey(key, -1, US_INV);
340931cb
A
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 }
f3c0d7a5
A
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:
f3c0d7a5
A
976 conflictingPattern.remove();
977 dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode);
4388f060
A
978 }
979 }
2ca993e8 980 }
f3c0d7a5 981};
46f4442e 982
f3c0d7a5
A
983// Virtual destructors must be defined out of line.
984DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
985DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
986DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}
987
988void
989DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) {
990 if (U_FAILURE(errorCode)) { return; }
991 UnicodeString rbPattern, value, field;
992 CharString path;
993
340931cb 994 UBool hasCountryFallbackResource = FALSE;
3d1f044b 995 LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &errorCode));
340931cb 996 LocalUResourceBundlePointer countryRB(ures_openWithCountryFallback(nullptr, locale.getName(), &hasCountryFallbackResource, &errorCode));
f3c0d7a5
A
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; }
f3c0d7a5
A
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; }
340931cb
A
1035 if (hasCountryFallbackResource) {
1036 AvailableFormatsSink countryAvailableFormatsSink(*this, TRUE);
1037 ures_getAllItemsWithFallback(countryRB.getAlias(), path.data(), countryAvailableFormatsSink, err);
1038 }
1039 AvailableFormatsSink availableFormatsSink(*this, FALSE);
f3c0d7a5 1040 ures_getAllItemsWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err);
46f4442e
A
1041}
1042
1043void
1044DateTimePatternGenerator::initHashtable(UErrorCode& err) {
3d1f044b
A
1045 if (U_FAILURE(err)) { return; }
1046 if (fAvailableFormatKeyHash!=nullptr) {
46f4442e
A
1047 return;
1048 }
3d1f044b
A
1049 LocalPointer<Hashtable> hash(new Hashtable(FALSE, err), err);
1050 if (U_SUCCESS(err)) {
1051 fAvailableFormatKeyHash = hash.orphan();
46f4442e
A
1052 }
1053}
1054
46f4442e
A
1055void
1056DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) {
1057 appendItemFormats[field] = value;
1058 // NUL-terminate for the C API.
1059 appendItemFormats[field].getTerminatedBuffer();
1060}
1061
1062const UnicodeString&
1063DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const {
1064 return appendItemFormats[field];
1065}
1066
1067void
1068DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) {
0f5d89e8 1069 setFieldDisplayName(field, UDATPG_WIDTH_APPENDITEM, value);
46f4442e
A
1070}
1071
1072const UnicodeString&
f3c0d7a5 1073DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const {
0f5d89e8
A
1074 return fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
1075}
1076
1077void
1078DateTimePatternGenerator::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
1084UnicodeString
1085DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const {
1086 return fieldDisplayNames[field][width];
f3c0d7a5
A
1087}
1088
1089UnicodeString&
0f5d89e8
A
1090DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) {
1091 return fieldDisplayNames[field][width];
46f4442e
A
1092}
1093
1094void
1095DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) {
1096 value = SINGLE_QUOTE;
0f5d89e8 1097 value += fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
46f4442e
A
1098 value += SINGLE_QUOTE;
1099}
1100
1101UnicodeString
1102DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) {
729e4ab9
A
1103 return getBestPattern(patternForm, UDATPG_MATCH_NO_OPTIONS, status);
1104}
1105
1106UnicodeString
1107DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDateTimePatternMatchOptions options, UErrorCode& status) {
3d1f044b
A
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;
46f4442e
A
1116 UnicodeString dtFormat;
1117 UnicodeString resultPattern;
57a6839d 1118 int32_t flags = kDTPGNoFlags;
46f4442e
A
1119
1120 int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
1121 int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
729e4ab9 1122
2ca993e8 1123 // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
0f5d89e8
A
1124 UnicodeString patternFormMapped = mapSkeletonMetacharacters(patternForm, &flags, options, status);
1125 if (U_FAILURE(status)) {
1126 return UnicodeString();
57a6839d 1127 }
729e4ab9 1128
46f4442e 1129 resultPattern.remove();
0f5d89e8 1130 dtMatcher->set(patternFormMapped, fp);
3d1f044b
A
1131 const PtnSkeleton* specifiedSkeleton = nullptr;
1132 bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, status, &specifiedSkeleton);
340931cb
A
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
3d1f044b
A
1141 if (U_FAILURE(status)) {
1142 return UnicodeString();
1143 }
1144
46f4442e 1145 if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) {
57a6839d 1146 resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options);
46f4442e
A
1147
1148 return resultPattern;
1149 }
1150 int32_t neededFields = dtMatcher->getFieldMask();
3d1f044b
A
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 }
46f4442e
A
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();
2ca993e8 1170 SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status);
46f4442e
A
1171 return resultPattern;
1172}
1173
0f5d89e8
A
1174/*
1175 * Map a skeleton that may have metacharacters jJC to one without, by replacing
3d1f044b 1176 * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B
0f5d89e8
A
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 */
1181UnicodeString
1182DateTimePatternGenerator::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 {
3d1f044b 1217 AllowedHourFormat bestAllowed;
0f5d89e8 1218 if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
3d1f044b 1219 bestAllowed = (AllowedHourFormat)fAllowedHourFormats[0];
0f5d89e8
A
1220 } else {
1221 status = U_INVALID_FORMAT_ERROR;
1222 return UnicodeString();
1223 }
3d1f044b 1224 if (bestAllowed == ALLOWED_HOUR_FORMAT_H || bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_Hb) {
0f5d89e8 1225 hourChar = CAP_H;
3d1f044b
A
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;
0f5d89e8
A
1230 }
1231 // in #13183 just add b/B to skeleton, no longer need to set special flags
3d1f044b 1232 if (bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_hB || bestAllowed == ALLOWED_HOUR_FORMAT_KB) {
0f5d89e8 1233 dayPeriodChar = CAP_B;
3d1f044b 1234 } else if (bestAllowed == ALLOWED_HOUR_FORMAT_Hb || bestAllowed == ALLOWED_HOUR_FORMAT_hb || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) {
0f5d89e8
A
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
46f4442e 1260UnicodeString
729e4ab9
A
1261DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
1262 const UnicodeString& skeleton,
1263 UErrorCode& status) {
1264 return replaceFieldTypes(pattern, skeleton, UDATPG_MATCH_NO_OPTIONS, status);
1265}
1266
1267UnicodeString
1268DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
1269 const UnicodeString& skeleton,
1270 UDateTimePatternMatchOptions options,
3d1f044b
A
1271 UErrorCode& status) {
1272 if (U_FAILURE(status)) {
1273 return UnicodeString();
1274 }
1275 if (U_FAILURE(internalErrorCode)) {
1276 status = internalErrorCode;
1277 return UnicodeString();
1278 }
46f4442e 1279 dtMatcher->set(skeleton, fp);
3d1f044b 1280 UnicodeString result = adjustFieldTypes(pattern, nullptr, kDTPGNoFlags, options);
46f4442e
A
1281 return result;
1282}
1283
1284void
1285DateTimePatternGenerator::setDecimal(const UnicodeString& newDecimal) {
1286 this->decimal = newDecimal;
1287 // NUL-terminate for the C API.
1288 this->decimal.getTerminatedBuffer();
1289}
1290
1291const UnicodeString&
1292DateTimePatternGenerator::getDecimal() const {
1293 return decimal;
1294}
1295
1296void
f3c0d7a5
A
1297DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) {
1298 if (U_FAILURE(status)) { return; }
46f4442e 1299 UnicodeString conflictingPattern;
46f4442e
A
1300
1301 for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) {
f3c0d7a5
A
1302 if (Canonical_Items[i] > 0) {
1303 addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
1304 }
1305 if (U_FAILURE(status)) { return; }
46f4442e
A
1306 }
1307}
1308
1309void
1310DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) {
1311 dateTimeFormat = dtFormat;
1312 // NUL-terminate for the C API.
1313 dateTimeFormat.getTerminatedBuffer();
1314}
1315
1316const UnicodeString&
1317DateTimePatternGenerator::getDateTimeFormat() const {
1318 return dateTimeFormat;
1319}
1320
1321void
1322DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCode& status) {
3d1f044b
A
1323 if (U_FAILURE(status)) { return; }
1324
46f4442e
A
1325 const UChar *resStr;
1326 int32_t resStrLen = 0;
729e4ab9 1327
3d1f044b 1328 LocalPointer<Calendar> fCalendar(Calendar::createInstance(locale, status), status);
f3c0d7a5
A
1329 if (U_FAILURE(status)) { return; }
1330
3d1f044b
A
1331 LocalUResourceBundlePointer calData(ures_open(nullptr, locale.getBaseName(), &status));
1332 if (U_FAILURE(status)) { return; }
f3c0d7a5 1333 ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status);
3d1f044b 1334 if (U_FAILURE(status)) { return; }
f3c0d7a5
A
1335
1336 LocalUResourceBundlePointer dateTimePatterns;
3d1f044b 1337 if (fCalendar->getType() != nullptr && *fCalendar->getType() != '\0'
f3c0d7a5
A
1338 && uprv_strcmp(fCalendar->getType(), DT_DateTimeGregorianTag) != 0) {
1339 dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), fCalendar->getType(),
3d1f044b 1340 nullptr, &status));
f3c0d7a5
A
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; }
46f4442e 1353
f3c0d7a5 1354 if (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTime)
46f4442e
A
1355 {
1356 status = U_INVALID_FORMAT_ERROR;
1357 return;
1358 }
f3c0d7a5 1359 resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), (int32_t)DateFormat::kDateTime, &resStrLen, &status);
46f4442e 1360 setDateTimeFormat(UnicodeString(TRUE, resStr, resStrLen));
46f4442e
A
1361}
1362
1363void
1364DateTimePatternGenerator::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
1373UDateTimePatternConflict
1374DateTimePatternGenerator::addPattern(
1375 const UnicodeString& pattern,
1376 UBool override,
1377 UnicodeString &conflictingPattern,
1378 UErrorCode& status)
1379{
3d1f044b
A
1380 if (U_FAILURE(internalErrorCode)) {
1381 status = internalErrorCode;
1382 return UDATPG_NO_CONFLICT;
1383 }
1384
1385 return addPatternWithSkeleton(pattern, nullptr, override, conflictingPattern, status);
46f4442e
A
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).
1398UDateTimePatternConflict
1399DateTimePatternGenerator::addPatternWithSkeleton(
1400 const UnicodeString& pattern,
1401 const UnicodeString* skeletonToUse,
1402 UBool override,
1403 UnicodeString& conflictingPattern,
1404 UErrorCode& status)
1405{
3d1f044b
A
1406 if (U_FAILURE(internalErrorCode)) {
1407 status = internalErrorCode;
1408 return UDATPG_NO_CONFLICT;
1409 }
46f4442e
A
1410
1411 UnicodeString basePattern;
1412 PtnSkeleton skeleton;
1413 UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT;
1414
1415 DateTimeMatcher matcher;
3d1f044b 1416 if ( skeletonToUse == nullptr ) {
46f4442e
A
1417 matcher.set(pattern, fp, skeleton);
1418 matcher.getBasePattern(basePattern);
1419 } else {
4388f060 1420 matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930
46f4442e
A
1421 matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse;
1422 }
51004dcb
A
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.
46f4442e
A
1430 UBool entryHadSpecifiedSkeleton;
1431 const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton);
3d1f044b 1432 if (duplicatePattern != nullptr && (!entryHadSpecifiedSkeleton || (skeletonToUse != nullptr && !override))) {
46f4442e
A
1433 conflictingStatus = UDATPG_BASE_CONFLICT;
1434 conflictingPattern = *duplicatePattern;
51004dcb 1435 if (!override) {
46f4442e
A
1436 return conflictingStatus;
1437 }
1438 }
51004dcb
A
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.
3d1f044b 1443 const PtnSkeleton* entrySpecifiedSkeleton = nullptr;
46f4442e 1444 duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton);
3d1f044b 1445 if (duplicatePattern != nullptr ) {
46f4442e
A
1446 conflictingStatus = UDATPG_CONFLICT;
1447 conflictingPattern = *duplicatePattern;
3d1f044b 1448 if (!override || (skeletonToUse != nullptr && entrySpecifiedSkeleton != nullptr)) {
46f4442e
A
1449 return conflictingStatus;
1450 }
1451 }
3d1f044b 1452 patternMap->add(basePattern, skeleton, pattern, skeletonToUse != nullptr, status);
46f4442e
A
1453 if(U_FAILURE(status)) {
1454 return conflictingStatus;
1455 }
729e4ab9 1456
46f4442e
A
1457 return UDATPG_NO_CONFLICT;
1458}
1459
1460
1461UDateTimePatternField
1462DateTimePatternGenerator::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
1471UDateTimePatternField
0f5d89e8
A
1472DateTimePatternGenerator::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 }
46f4442e 1487 for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
0f5d89e8 1488 if (uprv_strcmp(CLDR_FIELD_NAME[i],cldrFieldKey)==0) {
46f4442e
A
1489 return (UDateTimePatternField)i;
1490 }
1491 }
1492 return UDATPG_FIELD_COUNT;
1493}
1494
1495const UnicodeString*
1496DateTimePatternGenerator::getBestRaw(DateTimeMatcher& source,
1497 int32_t includeMask,
1498 DistanceInfo* missingFields,
3d1f044b 1499 UErrorCode &status,
46f4442e
A
1500 const PtnSkeleton** specifiedSkeletonPtr) {
1501 int32_t bestDistance = 0x7fffffff;
1502 DistanceInfo tempInfo;
3d1f044b
A
1503 const UnicodeString *bestPattern=nullptr;
1504 const PtnSkeleton* specifiedSkeleton=nullptr;
1505
1506 PatternMapIterator it(status);
1507 if (U_FAILURE(status)) { return nullptr; }
46f4442e 1508
46f4442e
A
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
1534UnicodeString
1535DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
1536 const PtnSkeleton* specifiedSkeleton,
57a6839d 1537 int32_t flags,
729e4ab9 1538 UDateTimePatternMatchOptions options) {
46f4442e
A
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;
2ca993e8 1561
0f5d89e8 1562 // handle day periods - with #13183, no longer need special handling here, integrated with normal types
2ca993e8 1563
57a6839d 1564 if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) {
f3c0d7a5
A
1565 field += decimal;
1566 dtMatcher->skeleton.original.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD, field);
729e4ab9 1567 } else if (dtMatcher->skeleton.type[typeValue]!=0) {
46f4442e
A
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
729e4ab9 1574 // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
4388f060
A
1575 // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
1576 // of characters from the found pattern.
46f4442e
A
1577 //
1578 // The length of the adjusted field (adjFieldLen) should match that in the originally
729e4ab9
A
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.
46f4442e 1589
f3c0d7a5
A
1590 UChar reqFieldChar = dtMatcher->skeleton.original.getFieldChar(typeValue);
1591 int32_t reqFieldLen = dtMatcher->skeleton.original.getFieldLength(typeValue);
1592 if (reqFieldChar == CAP_E && reqFieldLen < 3)
729e4ab9 1593 reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e
46f4442e 1594 int32_t adjFieldLen = reqFieldLen;
729e4ab9
A
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) {
f3c0d7a5 1600 int32_t skelFieldLen = specifiedSkeleton->original.getFieldLength(typeValue);
46f4442e
A
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 }
f3c0d7a5
A
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);
57a6839d
A
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 }
46f4442e 1635 field.remove();
3d1f044b
A
1636 for (int32_t j=adjFieldLen; j>0; --j) {
1637 field += c;
46f4442e 1638 }
46f4442e 1639 }
729e4ab9 1640 newPattern+=field;
46f4442e
A
1641 }
1642 }
1643 return newPattern;
1644}
1645
1646UnicodeString
3d1f044b
A
1647DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UErrorCode &status, UDateTimePatternMatchOptions options) {
1648 if (U_FAILURE(status)) {
1649 return UnicodeString();
1650 }
729e4ab9 1651 UnicodeString resultPattern, tempPattern;
3d1f044b 1652 const UnicodeString* tempPatternPtr;
46f4442e
A
1653 int32_t lastMissingFieldMask=0;
1654 if (missingFields!=0) {
1655 resultPattern=UnicodeString();
3d1f044b
A
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;
57a6839d 1662 resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options);
46f4442e
A
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)) {
57a6839d 1672 resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options);
46f4442e
A
1673 distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK;
1674 continue;
1675 }
1676 int32_t startingMask = distanceInfo->missingFieldMask;
3d1f044b
A
1677 tempPatternPtr = getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, status, &specifiedSkeleton);
1678 if (U_FAILURE(status)) {
1679 return UnicodeString();
1680 }
1681 tempPattern = *tempPatternPtr;
57a6839d 1682 tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options);
46f4442e
A
1683 int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask;
1684 int32_t topField=getTopBitNumber(foundMask);
3d1f044b
A
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 }
46f4442e
A
1697 lastMissingFieldMask = distanceInfo->missingFieldMask;
1698 }
1699 }
729e4ab9 1700 return resultPattern;
46f4442e
A
1701}
1702
1703int32_t
3d1f044b 1704DateTimePatternGenerator::getTopBitNumber(int32_t foundMask) const {
46f4442e
A
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 }
729e4ab9 1716 else
46f4442e
A
1717 return i-1;
1718}
1719
1720void
1721DateTimePatternGenerator::setAvailableFormat(const UnicodeString &key, UErrorCode& err)
1722{
1723 fAvailableFormatKeyHash->puti(key, 1, err);
1724}
1725
1726UBool
1727DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString &key) const {
1728 return (UBool)(fAvailableFormatKeyHash->geti(key) == 1);
1729}
1730
1731void
1732DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) {
3d1f044b 1733 if (other == nullptr || U_FAILURE(status)) {
46f4442e
A
1734 return;
1735 }
3d1f044b 1736 if (fAvailableFormatKeyHash != nullptr) {
46f4442e 1737 delete fAvailableFormatKeyHash;
3d1f044b 1738 fAvailableFormatKeyHash = nullptr;
46f4442e
A
1739 }
1740 initHashtable(status);
1741 if(U_FAILURE(status)){
1742 return;
1743 }
b331163b 1744 int32_t pos = UHASH_FIRST;
3d1f044b 1745 const UHashElement* elem = nullptr;
46f4442e 1746 // walk through the hash table and create a deep clone
3d1f044b 1747 while((elem = other->nextElement(pos))!= nullptr){
46f4442e
A
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
1757StringEnumeration*
1758DateTimePatternGenerator::getSkeletons(UErrorCode& status) const {
3d1f044b
A
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;
46f4442e
A
1770}
1771
1772const UnicodeString&
1773DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString& skeleton) const {
1774 PtnElem *curElem;
729e4ab9 1775
46f4442e
A
1776 if (skeleton.length() ==0) {
1777 return emptyString;
729e4ab9 1778 }
46f4442e 1779 curElem = patternMap->getHeader(skeleton.charAt(0));
3d1f044b 1780 while ( curElem != nullptr ) {
46f4442e
A
1781 if ( curElem->skeleton->getSkeleton()==skeleton ) {
1782 return curElem->pattern;
1783 }
3d1f044b 1784 curElem = curElem->next.getAlias();
46f4442e
A
1785 }
1786 return emptyString;
1787}
1788
1789StringEnumeration*
1790DateTimePatternGenerator::getBaseSkeletons(UErrorCode& status) const {
3d1f044b
A
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;
46f4442e
A
1802}
1803
1804StringEnumeration*
1805DateTimePatternGenerator::getRedundants(UErrorCode& status) {
3d1f044b
A
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; }
46f4442e 1813 const UnicodeString *pattern;
3d1f044b
A
1814 PatternMapIterator it(status);
1815 if (U_FAILURE(status)) { return nullptr; }
1816
46f4442e
A
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 }
3d1f044b 1823 if ( skipMatcher == nullptr ) {
46f4442e 1824 skipMatcher = new DateTimeMatcher(current);
3d1f044b
A
1825 if (skipMatcher == nullptr) {
1826 status = U_MEMORY_ALLOCATION_ERROR;
1827 return nullptr;
1828 }
46f4442e
A
1829 }
1830 else {
1831 *skipMatcher = current;
1832 }
1833 UnicodeString trial = getBestPattern(current.getPattern(), status);
3d1f044b 1834 if (U_FAILURE(status)) { return nullptr; }
729e4ab9 1835 if (trial == *pattern) {
3d1f044b
A
1836 ((DTRedundantEnumeration *)output.getAlias())->add(*pattern, status);
1837 if (U_FAILURE(status)) { return nullptr; }
46f4442e
A
1838 }
1839 if (current.equals(skipMatcher)) {
1840 continue;
1841 }
1842 }
3d1f044b 1843 return output.orphan();
46f4442e
A
1844}
1845
1846UBool
1847DateTimePatternGenerator::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
1860DateTimePatternGenerator*
1861DateTimePatternGenerator::clone() const {
1862 return new DateTimePatternGenerator(*this);
1863}
1864
1865PatternMap::PatternMap() {
1866 for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) {
3d1f044b 1867 boot[i] = nullptr;
46f4442e
A
1868 }
1869 isDupAllowed = TRUE;
1870}
1871
1872void
1873PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) {
3d1f044b
A
1874 if (U_FAILURE(status)) {
1875 return;
1876 }
46f4442e 1877 this->isDupAllowed = other.isDupAllowed;
3d1f044b
A
1878 for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) {
1879 PtnElem *curElem, *otherElem, *prevElem=nullptr;
46f4442e 1880 otherElem = other.boot[bootIndex];
3d1f044b
A
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
46f4442e 1885 }
3d1f044b
A
1886 newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(*(otherElem->skeleton)), status);
1887 if (U_FAILURE(status)) {
1888 return; // out of memory
46f4442e 1889 }
3d1f044b
A
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 }
46f4442e 1905 }
46f4442e 1906 prevElem = curElem;
3d1f044b 1907 otherElem = otherElem->next.getAlias();
46f4442e
A
1908 }
1909
1910 }
1911}
1912
729e4ab9 1913PtnElem*
3d1f044b 1914PatternMap::getHeader(UChar baseChar) const {
46f4442e 1915 PtnElem* curElem;
729e4ab9 1916
46f4442e
A
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 {
3d1f044b 1925 return nullptr;
46f4442e
A
1926 }
1927 }
1928 return curElem;
1929}
729e4ab9 1930
46f4442e
A
1931PatternMap::~PatternMap() {
1932 for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) {
3d1f044b 1933 if (boot[i] != nullptr ) {
46f4442e 1934 delete boot[i];
3d1f044b 1935 boot[i] = nullptr;
46f4442e
A
1936 }
1937 }
1938} // PatternMap destructor
1939
1940void
1941PatternMap::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
3d1f044b
A
1964 if (baseElem == nullptr) {
1965 LocalPointer<PtnElem> newElem(new PtnElem(basePattern, value), status);
1966 if (U_FAILURE(status)) {
1967 return; // out of memory
46f4442e 1968 }
3d1f044b
A
1969 newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status);
1970 if (U_FAILURE(status)) {
1971 return; // out of memory
1972 }
1973 newElem->skeletonWasSpecified = skeletonWasSpecified;
46f4442e 1974 if (baseChar >= LOW_A) {
3d1f044b 1975 boot[26 + (baseChar - LOW_A)] = newElem.orphan(); // the boot array now owns the PtnElem.
46f4442e
A
1976 }
1977 else {
3d1f044b 1978 boot[baseChar - CAP_A] = newElem.orphan(); // the boot array now owns the PtnElem.
46f4442e 1979 }
46f4442e 1980 }
3d1f044b 1981 if ( baseElem != nullptr ) {
46f4442e
A
1982 curElem = getDuplicateElem(basePattern, skeleton, baseElem);
1983
3d1f044b 1984 if (curElem == nullptr) {
46f4442e
A
1985 // add new element to the list.
1986 curElem = baseElem;
3d1f044b 1987 while( curElem -> next != nullptr )
46f4442e 1988 {
3d1f044b 1989 curElem = curElem->next.getAlias();
46f4442e 1990 }
3d1f044b
A
1991
1992 LocalPointer<PtnElem> newElem(new PtnElem(basePattern, value), status);
1993 if (U_FAILURE(status)) {
1994 return; // out of memory
46f4442e 1995 }
3d1f044b
A
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();
46f4442e
A
2003 }
2004 else {
2005 // Pattern exists in the list already.
2006 if ( !isDupAllowed ) {
2007 return;
2008 }
2009 // Overwrite the value.
2010 curElem->pattern = value;
51004dcb
A
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;
46f4442e
A
2014 }
2015 }
2016} // PatternMap::add
2017
2018// Find the pattern from the given basePattern string.
2019const UnicodeString *
3d1f044b 2020PatternMap::getPatternFromBasePattern(const UnicodeString& basePattern, UBool& skeletonWasSpecified) const { // key to search for
46f4442e
A
2021 PtnElem *curElem;
2022
3d1f044b
A
2023 if ((curElem=getHeader(basePattern.charAt(0)))==nullptr) {
2024 return nullptr; // no match
46f4442e
A
2025 }
2026
2027 do {
2028 if ( basePattern.compare(curElem->basePattern)==0 ) {
2029 skeletonWasSpecified = curElem->skeletonWasSpecified;
2030 return &(curElem->pattern);
2031 }
3d1f044b
A
2032 curElem = curElem->next.getAlias();
2033 } while (curElem != nullptr);
46f4442e 2034
3d1f044b 2035 return nullptr;
46f4442e
A
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.
2045const UnicodeString *
3d1f044b 2046PatternMap::getPatternFromSkeleton(const PtnSkeleton& skeleton, const PtnSkeleton** specifiedSkeletonPtr) const { // key to search for
46f4442e
A
2047 PtnElem *curElem;
2048
2049 if (specifiedSkeletonPtr) {
3d1f044b 2050 *specifiedSkeletonPtr = nullptr;
46f4442e
A
2051 }
2052
2053 // find boot entry
f3c0d7a5 2054 UChar baseChar = skeleton.getFirstChar();
3d1f044b
A
2055 if ((curElem=getHeader(baseChar))==nullptr) {
2056 return nullptr; // no match
46f4442e
A
2057 }
2058
2059 do {
f3c0d7a5 2060 UBool equal;
3d1f044b 2061 if (specifiedSkeletonPtr != nullptr) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
f3c0d7a5 2062 equal = curElem->skeleton->original == skeleton.original;
46f4442e 2063 } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
f3c0d7a5 2064 equal = curElem->skeleton->baseOriginal == skeleton.baseOriginal;
46f4442e 2065 }
f3c0d7a5 2066 if (equal) {
46f4442e 2067 if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) {
3d1f044b 2068 *specifiedSkeletonPtr = curElem->skeleton.getAlias();
46f4442e
A
2069 }
2070 return &(curElem->pattern);
2071 }
3d1f044b
A
2072 curElem = curElem->next.getAlias();
2073 } while (curElem != nullptr);
46f4442e 2074
3d1f044b 2075 return nullptr;
729e4ab9 2076}
46f4442e
A
2077
2078UBool
3d1f044b 2079PatternMap::equals(const PatternMap& other) const {
46f4442e
A
2080 if ( this==&other ) {
2081 return TRUE;
2082 }
3d1f044b
A
2083 for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) {
2084 if (boot[bootIndex] == other.boot[bootIndex]) {
46f4442e
A
2085 continue;
2086 }
3d1f044b 2087 if ((boot[bootIndex] == nullptr) || (other.boot[bootIndex] == nullptr)) {
46f4442e
A
2088 return FALSE;
2089 }
2090 PtnElem *otherElem = other.boot[bootIndex];
2091 PtnElem *myElem = boot[bootIndex];
3d1f044b 2092 while ((otherElem != nullptr) || (myElem != nullptr)) {
46f4442e
A
2093 if ( myElem == otherElem ) {
2094 break;
2095 }
3d1f044b 2096 if ((otherElem == nullptr) || (myElem == nullptr)) {
46f4442e
A
2097 return FALSE;
2098 }
2099 if ( (myElem->basePattern != otherElem->basePattern) ||
2100 (myElem->pattern != otherElem->pattern) ) {
2101 return FALSE;
2102 }
3d1f044b 2103 if ((myElem->skeleton.getAlias() != otherElem->skeleton.getAlias()) &&
46f4442e
A
2104 !myElem->skeleton->equals(*(otherElem->skeleton))) {
2105 return FALSE;
2106 }
3d1f044b
A
2107 myElem = myElem->next.getAlias();
2108 otherElem = otherElem->next.getAlias();
46f4442e
A
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.
2116PtnElem*
2117PatternMap::getDuplicateElem(
2118 const UnicodeString &basePattern,
2119 const PtnSkeleton &skeleton,
3d1f044b 2120 PtnElem *baseElem) {
46f4442e
A
2121 PtnElem *curElem;
2122
3d1f044b
A
2123 if ( baseElem == nullptr ) {
2124 return nullptr;
46f4442e
A
2125 }
2126 else {
2127 curElem = baseElem;
2128 }
2129 do {
2130 if ( basePattern.compare(curElem->basePattern)==0 ) {
3d1f044b
A
2131 UBool isEqual = TRUE;
2132 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
46f4442e 2133 if (curElem->skeleton->type[i] != skeleton.type[i] ) {
3d1f044b 2134 isEqual = FALSE;
46f4442e
A
2135 break;
2136 }
2137 }
2138 if (isEqual) {
2139 return curElem;
2140 }
2141 }
3d1f044b
A
2142 curElem = curElem->next.getAlias();
2143 } while( curElem != nullptr );
46f4442e
A
2144
2145 // end of the list
3d1f044b 2146 return nullptr;
46f4442e
A
2147
2148} // PatternMap::getDuplicateElem
2149
2150DateTimeMatcher::DateTimeMatcher(void) {
2151}
2152
4388f060
A
2153DateTimeMatcher::~DateTimeMatcher() {}
2154
46f4442e
A
2155DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) {
2156 copyFrom(other.skeleton);
2157}
2158
2159
2160void
2161DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp) {
2162 PtnSkeleton localSkeleton;
2163 return set(pattern, fp, localSkeleton);
2164}
2165
2166void
2167DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) {
2168 int32_t i;
2169 for (i=0; i<UDATPG_FIELD_COUNT; ++i) {
f3c0d7a5 2170 skeletonResult.type[i] = NONE;
46f4442e 2171 }
0f5d89e8
A
2172 skeletonResult.original.clear();
2173 skeletonResult.baseOriginal.clear();
2174 skeletonResult.addedDefaultDayPeriod = FALSE;
2175
46f4442e
A
2176 fp->set(pattern);
2177 for (i=0; i < fp->itemNumber; i++) {
f3c0d7a5 2178 const UnicodeString& value = fp->items[i];
0f5d89e8 2179 // don't skip 'a' anymore, dayPeriod handled specially below
46f4442e 2180
f3c0d7a5 2181 if ( fp->isQuoteLiteral(value) ) {
46f4442e
A
2182 UnicodeString quoteLiteral;
2183 fp->getQuoteLiteral(quoteLiteral, &i);
2184 continue;
2185 }
f3c0d7a5 2186 int32_t canonicalIndex = fp->getCanonicalIndex(value);
3d1f044b 2187 if (canonicalIndex < 0) {
46f4442e
A
2188 continue;
2189 }
2190 const dtTypeElem *row = &dtTypes[canonicalIndex];
f3c0d7a5
A
2191 int32_t field = row->field;
2192 skeletonResult.original.populate(field, value);
46f4442e 2193 UChar repeatChar = row->patternChar;
0f5d89e8 2194 int32_t repeatCount = row->minLen;
f3c0d7a5
A
2195 skeletonResult.baseOriginal.populate(field, repeatChar, repeatCount);
2196 int16_t subField = row->type;
3d1f044b
A
2197 if (row->type > 0) {
2198 U_ASSERT(value.length() < INT16_MAX);
2199 subField += static_cast<int16_t>(value.length());
46f4442e 2200 }
f3c0d7a5 2201 skeletonResult.type[field] = subField;
46f4442e 2202 }
0f5d89e8
A
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 }
46f4442e
A
2227 copyFrom(skeletonResult);
2228}
2229
2230void
2231DateTimeMatcher::getBasePattern(UnicodeString &result ) {
2232 result.remove(); // Reset the result first.
f3c0d7a5 2233 skeleton.baseOriginal.appendTo(result);
46f4442e
A
2234}
2235
2236UnicodeString
2237DateTimeMatcher::getPattern() {
2238 UnicodeString result;
f3c0d7a5 2239 return skeleton.original.appendTo(result);
46f4442e
A
2240}
2241
2242int32_t
3d1f044b
A
2243DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask, DistanceInfo& distanceInfo) const {
2244 int32_t result = 0;
46f4442e
A
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
2270void
2271DateTimeMatcher::copyFrom(const PtnSkeleton& newSkeleton) {
f3c0d7a5 2272 skeleton.copyFrom(newSkeleton);
46f4442e
A
2273}
2274
2275void
2276DateTimeMatcher::copyFrom() {
2277 // same as clear
f3c0d7a5 2278 skeleton.clear();
46f4442e
A
2279}
2280
2281UBool
2282DateTimeMatcher::equals(const DateTimeMatcher* other) const {
3d1f044b 2283 if (other==nullptr) { return FALSE; }
f3c0d7a5 2284 return skeleton.original == other->skeleton.original;
46f4442e
A
2285}
2286
2287int32_t
3d1f044b
A
2288DateTimeMatcher::getFieldMask() const {
2289 int32_t result = 0;
46f4442e
A
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
2299PtnSkeleton*
2300DateTimeMatcher::getSkeletonPtr() {
2301 return &skeleton;
2302}
2303
2304FormatParser::FormatParser () {
2305 status = START;
3d1f044b 2306 itemNumber = 0;
46f4442e
A
2307}
2308
2309
2310FormatParser::~FormatParser () {
2311}
2312
2313
2314// Find the next token with the starting position and length
2315// Note: the startPos may
2316FormatParser::TokenStatus
2317FormatParser::setTokens(const UnicodeString& pattern, int32_t startPos, int32_t *len) {
3d1f044b 2318 int32_t curLoc = startPos;
46f4442e
A
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
2342void
2343FormatParser::set(const UnicodeString& pattern) {
3d1f044b
A
2344 int32_t startPos = 0;
2345 TokenStatus result = START;
2346 int32_t len = 0;
2347 itemNumber = 0;
46f4442e
A
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
2362int32_t
729e4ab9 2363FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) {
46f4442e 2364 int32_t len = s.length();
729e4ab9
A
2365 if (len == 0) {
2366 return -1;
2367 }
46f4442e 2368 UChar ch = s.charAt(0);
46f4442e 2369
729e4ab9
A
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;
f3c0d7a5 2378 while (dtTypes[i].patternChar != 0x0000) {
729e4ab9 2379 if ( dtTypes[i].patternChar != ch ) {
46f4442e
A
2380 ++i;
2381 continue;
2382 }
729e4ab9
A
2383 bestRow = i;
2384 if (dtTypes[i].patternChar != dtTypes[i+1].patternChar) {
46f4442e
A
2385 return i;
2386 }
2387 if (dtTypes[i+1].minLen <= len) {
2388 ++i;
2389 continue;
2390 }
2391 return i;
2392 }
729e4ab9 2393 return strict ? -1 : bestRow;
46f4442e
A
2394}
2395
2396UBool
2ca993e8 2397FormatParser::isQuoteLiteral(const UnicodeString& s) {
3d1f044b 2398 return (UBool)(s.charAt(0) == SINGLE_QUOTE);
46f4442e
A
2399}
2400
3d1f044b 2401// This function assumes the current itemIndex points to the quote literal.
46f4442e
A
2402// Please call isQuoteLiteral prior to this function.
2403void
2404FormatParser::getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex) {
3d1f044b 2405 int32_t i = *itemIndex;
729e4ab9 2406
46f4442e
A
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
2433UBool
3d1f044b 2434FormatParser::isPatternSeparator(const UnicodeString& field) const {
46f4442e
A
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
4388f060
A
2448DistanceInfo::~DistanceInfo() {}
2449
46f4442e 2450void
3d1f044b 2451DistanceInfo::setTo(const DistanceInfo& other) {
46f4442e
A
2452 missingFieldMask = other.missingFieldMask;
2453 extraFieldMask= other.extraFieldMask;
2454}
2455
3d1f044b
A
2456PatternMapIterator::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);
46f4442e
A
2461}
2462
46f4442e 2463PatternMapIterator::~PatternMapIterator() {
46f4442e
A
2464}
2465
2466void
2467PatternMapIterator::set(PatternMap& newPatternMap) {
2468 this->patternMap=&newPatternMap;
2469}
2470
729e4ab9 2471PtnSkeleton*
3d1f044b
A
2472PatternMapIterator::getSkeleton() const {
2473 if ( nodePtr == nullptr ) {
2474 return nullptr;
46f4442e
A
2475 }
2476 else {
3d1f044b 2477 return nodePtr->skeleton.getAlias();
46f4442e
A
2478 }
2479}
2480
2481UBool
3d1f044b
A
2482PatternMapIterator::hasNext() const {
2483 int32_t headIndex = bootIndex;
2484 PtnElem *curPtr = nodePtr;
46f4442e 2485
3d1f044b 2486 if (patternMap==nullptr) {
46f4442e
A
2487 return FALSE;
2488 }
2489 while ( headIndex < MAX_PATTERN_ENTRIES ) {
3d1f044b
A
2490 if ( curPtr != nullptr ) {
2491 if ( curPtr->next != nullptr ) {
46f4442e
A
2492 return TRUE;
2493 }
2494 else {
2495 headIndex++;
3d1f044b 2496 curPtr=nullptr;
46f4442e
A
2497 continue;
2498 }
2499 }
2500 else {
3d1f044b 2501 if ( patternMap->boot[headIndex] != nullptr ) {
46f4442e
A
2502 return TRUE;
2503 }
2504 else {
2505 headIndex++;
2506 continue;
2507 }
2508 }
46f4442e
A
2509 }
2510 return FALSE;
2511}
2512
2513DateTimeMatcher&
2514PatternMapIterator::next() {
2515 while ( bootIndex < MAX_PATTERN_ENTRIES ) {
3d1f044b
A
2516 if ( nodePtr != nullptr ) {
2517 if ( nodePtr->next != nullptr ) {
2518 nodePtr = nodePtr->next.getAlias();
46f4442e
A
2519 break;
2520 }
2521 else {
2522 bootIndex++;
3d1f044b 2523 nodePtr=nullptr;
46f4442e
A
2524 continue;
2525 }
2526 }
2527 else {
3d1f044b 2528 if ( patternMap->boot[bootIndex] != nullptr ) {
46f4442e
A
2529 nodePtr = patternMap->boot[bootIndex];
2530 break;
2531 }
2532 else {
2533 bootIndex++;
2534 continue;
2535 }
2536 }
2537 }
3d1f044b 2538 if (nodePtr!=nullptr) {
46f4442e
A
2539 matcher->copyFrom(*nodePtr->skeleton);
2540 }
2541 else {
2542 matcher->copyFrom();
2543 }
2544 return *matcher;
2545}
2546
f3c0d7a5
A
2547
2548SkeletonFields::SkeletonFields() {
2549 // Set initial values to zero
2550 clear();
46f4442e
A
2551}
2552
f3c0d7a5
A
2553void SkeletonFields::clear() {
2554 uprv_memset(chars, 0, sizeof(chars));
2555 uprv_memset(lengths, 0, sizeof(lengths));
2556}
46f4442e 2557
f3c0d7a5
A
2558void SkeletonFields::copyFrom(const SkeletonFields& other) {
2559 uprv_memcpy(chars, other.chars, sizeof(chars));
2560 uprv_memcpy(lengths, other.lengths, sizeof(lengths));
2561}
2562
2563void SkeletonFields::clearField(int32_t field) {
2564 chars[field] = 0;
2565 lengths[field] = 0;
2566}
2567
2568UChar SkeletonFields::getFieldChar(int32_t field) const {
2569 return chars[field];
2570}
2571
2572int32_t SkeletonFields::getFieldLength(int32_t field) const {
2573 return lengths[field];
2574}
2575
2576void SkeletonFields::populate(int32_t field, const UnicodeString& value) {
2577 populate(field, value.charAt(0), value.length());
2578}
2579
2580void SkeletonFields::populate(int32_t field, UChar ch, int32_t length) {
2581 chars[field] = (int8_t) ch;
2582 lengths[field] = (int8_t) length;
2583}
2584
2585UBool SkeletonFields::isFieldEmpty(int32_t field) const {
2586 return lengths[field] == 0;
2587}
2588
2589UnicodeString& SkeletonFields::appendTo(UnicodeString& string) const {
2590 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
2591 appendFieldTo(i, string);
46f4442e 2592 }
f3c0d7a5 2593 return string;
46f4442e
A
2594}
2595
f3c0d7a5
A
2596UnicodeString& 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
2606UChar SkeletonFields::getFirstChar() const {
2607 for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
2608 if (lengths[i] != 0) {
2609 return chars[i];
46f4442e
A
2610 }
2611 }
f3c0d7a5
A
2612 return '\0';
2613}
2614
2615
340931cb
A
2616PtnSkeleton::PtnSkeleton()
2617 : addedDefaultDayPeriod(FALSE) {
f3c0d7a5
A
2618}
2619
2620PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) {
2621 copyFrom(other);
2622}
2623
2624void PtnSkeleton::copyFrom(const PtnSkeleton& other) {
2625 uprv_memcpy(type, other.type, sizeof(type));
2626 original.copyFrom(other.original);
2627 baseOriginal.copyFrom(other.baseOriginal);
340931cb 2628 addedDefaultDayPeriod = other.addedDefaultDayPeriod;
f3c0d7a5
A
2629}
2630
2631void PtnSkeleton::clear() {
2632 uprv_memset(type, 0, sizeof(type));
2633 original.clear();
2634 baseOriginal.clear();
2635}
2636
2637UBool
2638PtnSkeleton::equals(const PtnSkeleton& other) const {
2639 return (original == other.original)
2640 && (baseOriginal == other.baseOriginal)
2641 && (uprv_memcmp(type, other.type, sizeof(type)) == 0);
46f4442e
A
2642}
2643
2644UnicodeString
f3c0d7a5 2645PtnSkeleton::getSkeleton() const {
46f4442e 2646 UnicodeString result;
0f5d89e8
A
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;
46f4442e
A
2655}
2656
2657UnicodeString
f3c0d7a5 2658PtnSkeleton::getBaseSkeleton() const {
46f4442e 2659 UnicodeString result;
0f5d89e8
A
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;
f3c0d7a5 2668}
46f4442e 2669
f3c0d7a5
A
2670UChar
2671PtnSkeleton::getFirstChar() const {
2672 return baseOriginal.getFirstChar();
46f4442e
A
2673}
2674
2675PtnSkeleton::~PtnSkeleton() {
2676}
2677
729e4ab9 2678PtnElem::PtnElem(const UnicodeString &basePat, const UnicodeString &pat) :
3d1f044b 2679 basePattern(basePat), skeleton(nullptr), pattern(pat), next(nullptr)
46f4442e
A
2680{
2681}
2682
2683PtnElem::~PtnElem() {
46f4442e
A
2684}
2685
3d1f044b 2686DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum type, UErrorCode& status) : fSkeletons(nullptr) {
46f4442e
A
2687 PtnElem *curElem;
2688 PtnSkeleton *curSkeleton;
2689 UnicodeString s;
2690 int32_t bootIndex;
2691
2692 pos=0;
3d1f044b 2693 fSkeletons.adoptInsteadAndCheckErrorCode(new UVector(status), status);
46f4442e 2694 if (U_FAILURE(status)) {
46f4442e
A
2695 return;
2696 }
3d1f044b 2697
46f4442e
A
2698 for (bootIndex=0; bootIndex<MAX_PATTERN_ENTRIES; ++bootIndex ) {
2699 curElem = patternMap.boot[bootIndex];
3d1f044b 2700 while (curElem!=nullptr) {
46f4442e
A
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:
3d1f044b 2709 curSkeleton=curElem->skeleton.getAlias();
46f4442e
A
2710 s=curSkeleton->getSkeleton();
2711 break;
2712 }
2713 if ( !isCanonicalItem(s) ) {
3d1f044b
A
2714 LocalPointer<UnicodeString> newElem(new UnicodeString(s), status);
2715 if (U_FAILURE(status)) {
2716 return;
2717 }
2718 fSkeletons->addElement(newElem.getAlias(), status);
46f4442e 2719 if (U_FAILURE(status)) {
3d1f044b 2720 fSkeletons.adoptInstead(nullptr);
46f4442e
A
2721 return;
2722 }
3d1f044b 2723 newElem.orphan(); // fSkeletons vector now owns the UnicodeString.
46f4442e 2724 }
3d1f044b 2725 curElem = curElem->next.getAlias();
46f4442e
A
2726 }
2727 }
3d1f044b 2728 if ((bootIndex==MAX_PATTERN_ENTRIES) && (curElem!=nullptr) ) {
46f4442e
A
2729 status = U_BUFFER_OVERFLOW_ERROR;
2730 }
2731}
2732
2733const UnicodeString*
2734DTSkeletonEnumeration::snext(UErrorCode& status) {
3d1f044b 2735 if (U_SUCCESS(status) && fSkeletons.isValid() && pos < fSkeletons->size()) {
46f4442e
A
2736 return (const UnicodeString*)fSkeletons->elementAt(pos++);
2737 }
3d1f044b 2738 return nullptr;
46f4442e
A
2739}
2740
2741void
2742DTSkeletonEnumeration::reset(UErrorCode& /*status*/) {
2743 pos=0;
2744}
2745
2746int32_t
2747DTSkeletonEnumeration::count(UErrorCode& /*status*/) const {
3d1f044b 2748 return (fSkeletons.isNull()) ? 0 : fSkeletons->size();
46f4442e
A
2749}
2750
2751UBool
2752DTSkeletonEnumeration::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
2764DTSkeletonEnumeration::~DTSkeletonEnumeration() {
2765 UnicodeString *s;
3d1f044b
A
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 }
46f4442e
A
2771 }
2772 }
46f4442e
A
2773}
2774
3d1f044b 2775DTRedundantEnumeration::DTRedundantEnumeration() : pos(0), fPatterns(nullptr) {
46f4442e
A
2776}
2777
2778void
2779DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) {
3d1f044b
A
2780 if (U_FAILURE(status)) { return; }
2781 if (fPatterns.isNull()) {
2782 fPatterns.adoptInsteadAndCheckErrorCode(new UVector(status), status);
46f4442e 2783 if (U_FAILURE(status)) {
46f4442e
A
2784 return;
2785 }
2786 }
3d1f044b
A
2787 LocalPointer<UnicodeString> newElem(new UnicodeString(pattern), status);
2788 if (U_FAILURE(status)) {
2789 return;
2790 }
2791 fPatterns->addElement(newElem.getAlias(), status);
46f4442e 2792 if (U_FAILURE(status)) {
3d1f044b 2793 fPatterns.adoptInstead(nullptr);
46f4442e
A
2794 return;
2795 }
3d1f044b 2796 newElem.orphan(); // fPatterns now owns the string.
46f4442e
A
2797}
2798
2799const UnicodeString*
2800DTRedundantEnumeration::snext(UErrorCode& status) {
3d1f044b 2801 if (U_SUCCESS(status) && fPatterns.isValid() && pos < fPatterns->size()) {
46f4442e
A
2802 return (const UnicodeString*)fPatterns->elementAt(pos++);
2803 }
3d1f044b 2804 return nullptr;
46f4442e
A
2805}
2806
2807void
2808DTRedundantEnumeration::reset(UErrorCode& /*status*/) {
2809 pos=0;
2810}
2811
2812int32_t
2813DTRedundantEnumeration::count(UErrorCode& /*status*/) const {
3d1f044b 2814 return (fPatterns.isNull()) ? 0 : fPatterns->size();
46f4442e
A
2815}
2816
2817UBool
3d1f044b 2818DTRedundantEnumeration::isCanonicalItem(const UnicodeString& item) const {
46f4442e
A
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
2830DTRedundantEnumeration::~DTRedundantEnumeration() {
2831 UnicodeString *s;
3d1f044b
A
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 }
46f4442e 2837 }
3d1f044b 2838 }
46f4442e
A
2839}
2840
340931cb
A
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 */
2847extern 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
46f4442e
A
2920U_NAMESPACE_END
2921
2922
2923#endif /* #if !UCONFIG_NO_FORMATTING */
2924
2925//eof