Coverage Report

Created: 2026-10-31 06:17

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