Coverage Report

Created: 2025-06-24 06:43

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