Coverage Report

Created: 2025-12-07 06:36

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