Bug Summary

File:out/../deps/icu-small/source/i18n/dtptngen.cpp
Warning:line 2459, column 16
Value stored to 'startPos' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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