/src/icu/source/i18n/number_skeletons.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // © 2018 and later: Unicode, Inc. and others. |
2 | | // License & terms of use: http://www.unicode.org/copyright.html |
3 | | |
4 | | #include "unicode/utypes.h" |
5 | | |
6 | | #if !UCONFIG_NO_FORMATTING |
7 | | |
8 | | // Allow implicit conversion from char16_t* to UnicodeString for this file: |
9 | | // Helpful in toString methods and elsewhere. |
10 | | #define UNISTR_FROM_STRING_EXPLICIT |
11 | | |
12 | | #include "number_decnum.h" |
13 | | #include "number_roundingutils.h" |
14 | | #include "number_skeletons.h" |
15 | | #include "umutex.h" |
16 | | #include "ucln_in.h" |
17 | | #include "patternprops.h" |
18 | | #include "unicode/ucharstriebuilder.h" |
19 | | #include "number_utils.h" |
20 | | #include "number_decimalquantity.h" |
21 | | #include "unicode/numberformatter.h" |
22 | | #include "uinvchar.h" |
23 | | #include "charstr.h" |
24 | | #include "string_segment.h" |
25 | | #include "unicode/errorcode.h" |
26 | | #include "util.h" |
27 | | #include "measunit_impl.h" |
28 | | |
29 | | using namespace icu; |
30 | | using namespace icu::number; |
31 | | using namespace icu::number::impl; |
32 | | using namespace icu::number::impl::skeleton; |
33 | | |
34 | | namespace { |
35 | | |
36 | | icu::UInitOnce gNumberSkeletonsInitOnce = U_INITONCE_INITIALIZER; |
37 | | |
38 | | char16_t* kSerializedStemTrie = nullptr; |
39 | | |
40 | 0 | UBool U_CALLCONV cleanupNumberSkeletons() { |
41 | 0 | uprv_free(kSerializedStemTrie); |
42 | 0 | kSerializedStemTrie = nullptr; |
43 | 0 | gNumberSkeletonsInitOnce.reset(); |
44 | 0 | return TRUE; |
45 | 0 | } |
46 | | |
47 | 0 | void U_CALLCONV initNumberSkeletons(UErrorCode& status) { |
48 | 0 | ucln_i18n_registerCleanup(UCLN_I18N_NUMBER_SKELETONS, cleanupNumberSkeletons); |
49 | |
|
50 | 0 | UCharsTrieBuilder b(status); |
51 | 0 | if (U_FAILURE(status)) { return; } |
52 | | |
53 | | // Section 1: |
54 | 0 | b.add(u"compact-short", STEM_COMPACT_SHORT, status); |
55 | 0 | b.add(u"compact-long", STEM_COMPACT_LONG, status); |
56 | 0 | b.add(u"scientific", STEM_SCIENTIFIC, status); |
57 | 0 | b.add(u"engineering", STEM_ENGINEERING, status); |
58 | 0 | b.add(u"notation-simple", STEM_NOTATION_SIMPLE, status); |
59 | 0 | b.add(u"base-unit", STEM_BASE_UNIT, status); |
60 | 0 | b.add(u"percent", STEM_PERCENT, status); |
61 | 0 | b.add(u"permille", STEM_PERMILLE, status); |
62 | 0 | b.add(u"precision-integer", STEM_PRECISION_INTEGER, status); |
63 | 0 | b.add(u"precision-unlimited", STEM_PRECISION_UNLIMITED, status); |
64 | 0 | b.add(u"precision-currency-standard", STEM_PRECISION_CURRENCY_STANDARD, status); |
65 | 0 | b.add(u"precision-currency-cash", STEM_PRECISION_CURRENCY_CASH, status); |
66 | 0 | b.add(u"rounding-mode-ceiling", STEM_ROUNDING_MODE_CEILING, status); |
67 | 0 | b.add(u"rounding-mode-floor", STEM_ROUNDING_MODE_FLOOR, status); |
68 | 0 | b.add(u"rounding-mode-down", STEM_ROUNDING_MODE_DOWN, status); |
69 | 0 | b.add(u"rounding-mode-up", STEM_ROUNDING_MODE_UP, status); |
70 | 0 | b.add(u"rounding-mode-half-even", STEM_ROUNDING_MODE_HALF_EVEN, status); |
71 | 0 | b.add(u"rounding-mode-half-odd", STEM_ROUNDING_MODE_HALF_ODD, status); |
72 | 0 | b.add(u"rounding-mode-half-ceiling", STEM_ROUNDING_MODE_HALF_CEILING, status); |
73 | 0 | b.add(u"rounding-mode-half-floor", STEM_ROUNDING_MODE_HALF_FLOOR, status); |
74 | 0 | b.add(u"rounding-mode-half-down", STEM_ROUNDING_MODE_HALF_DOWN, status); |
75 | 0 | b.add(u"rounding-mode-half-up", STEM_ROUNDING_MODE_HALF_UP, status); |
76 | 0 | b.add(u"rounding-mode-unnecessary", STEM_ROUNDING_MODE_UNNECESSARY, status); |
77 | 0 | b.add(u"integer-width-trunc", STEM_INTEGER_WIDTH_TRUNC, status); |
78 | 0 | b.add(u"group-off", STEM_GROUP_OFF, status); |
79 | 0 | b.add(u"group-min2", STEM_GROUP_MIN2, status); |
80 | 0 | b.add(u"group-auto", STEM_GROUP_AUTO, status); |
81 | 0 | b.add(u"group-on-aligned", STEM_GROUP_ON_ALIGNED, status); |
82 | 0 | b.add(u"group-thousands", STEM_GROUP_THOUSANDS, status); |
83 | 0 | b.add(u"latin", STEM_LATIN, status); |
84 | 0 | b.add(u"unit-width-narrow", STEM_UNIT_WIDTH_NARROW, status); |
85 | 0 | b.add(u"unit-width-short", STEM_UNIT_WIDTH_SHORT, status); |
86 | 0 | b.add(u"unit-width-full-name", STEM_UNIT_WIDTH_FULL_NAME, status); |
87 | 0 | b.add(u"unit-width-iso-code", STEM_UNIT_WIDTH_ISO_CODE, status); |
88 | 0 | b.add(u"unit-width-formal", STEM_UNIT_WIDTH_FORMAL, status); |
89 | 0 | b.add(u"unit-width-variant", STEM_UNIT_WIDTH_VARIANT, status); |
90 | 0 | b.add(u"unit-width-hidden", STEM_UNIT_WIDTH_HIDDEN, status); |
91 | 0 | b.add(u"sign-auto", STEM_SIGN_AUTO, status); |
92 | 0 | b.add(u"sign-always", STEM_SIGN_ALWAYS, status); |
93 | 0 | b.add(u"sign-never", STEM_SIGN_NEVER, status); |
94 | 0 | b.add(u"sign-accounting", STEM_SIGN_ACCOUNTING, status); |
95 | 0 | b.add(u"sign-accounting-always", STEM_SIGN_ACCOUNTING_ALWAYS, status); |
96 | 0 | b.add(u"sign-except-zero", STEM_SIGN_EXCEPT_ZERO, status); |
97 | 0 | b.add(u"sign-accounting-except-zero", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status); |
98 | 0 | b.add(u"sign-negative", STEM_SIGN_NEGATIVE, status); |
99 | 0 | b.add(u"sign-accounting-negative", STEM_SIGN_ACCOUNTING_NEGATIVE, status); |
100 | 0 | b.add(u"decimal-auto", STEM_DECIMAL_AUTO, status); |
101 | 0 | b.add(u"decimal-always", STEM_DECIMAL_ALWAYS, status); |
102 | 0 | if (U_FAILURE(status)) { return; } |
103 | | |
104 | | // Section 2: |
105 | 0 | b.add(u"precision-increment", STEM_PRECISION_INCREMENT, status); |
106 | 0 | b.add(u"measure-unit", STEM_MEASURE_UNIT, status); |
107 | 0 | b.add(u"per-measure-unit", STEM_PER_MEASURE_UNIT, status); |
108 | 0 | b.add(u"unit", STEM_UNIT, status); |
109 | 0 | b.add(u"usage", STEM_UNIT_USAGE, status); |
110 | 0 | b.add(u"currency", STEM_CURRENCY, status); |
111 | 0 | b.add(u"integer-width", STEM_INTEGER_WIDTH, status); |
112 | 0 | b.add(u"numbering-system", STEM_NUMBERING_SYSTEM, status); |
113 | 0 | b.add(u"scale", STEM_SCALE, status); |
114 | 0 | if (U_FAILURE(status)) { return; } |
115 | | |
116 | | // Section 3 (concise tokens): |
117 | 0 | b.add(u"K", STEM_COMPACT_SHORT, status); |
118 | 0 | b.add(u"KK", STEM_COMPACT_LONG, status); |
119 | 0 | b.add(u"%", STEM_PERCENT, status); |
120 | 0 | b.add(u"%x100", STEM_PERCENT_100, status); |
121 | 0 | b.add(u",_", STEM_GROUP_OFF, status); |
122 | 0 | b.add(u",?", STEM_GROUP_MIN2, status); |
123 | 0 | b.add(u",!", STEM_GROUP_ON_ALIGNED, status); |
124 | 0 | b.add(u"+!", STEM_SIGN_ALWAYS, status); |
125 | 0 | b.add(u"+_", STEM_SIGN_NEVER, status); |
126 | 0 | b.add(u"()", STEM_SIGN_ACCOUNTING, status); |
127 | 0 | b.add(u"()!", STEM_SIGN_ACCOUNTING_ALWAYS, status); |
128 | 0 | b.add(u"+?", STEM_SIGN_EXCEPT_ZERO, status); |
129 | 0 | b.add(u"()?", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status); |
130 | 0 | b.add(u"+-", STEM_SIGN_NEGATIVE, status); |
131 | 0 | b.add(u"()-", STEM_SIGN_ACCOUNTING_NEGATIVE, status); |
132 | 0 | if (U_FAILURE(status)) { return; } |
133 | | |
134 | | // Build the CharsTrie |
135 | | // TODO: Use SLOW or FAST here? |
136 | 0 | UnicodeString result; |
137 | 0 | b.buildUnicodeString(USTRINGTRIE_BUILD_FAST, result, status); |
138 | 0 | if (U_FAILURE(status)) { return; } |
139 | | |
140 | | // Copy the result into the global constant pointer |
141 | 0 | size_t numBytes = result.length() * sizeof(char16_t); |
142 | 0 | kSerializedStemTrie = static_cast<char16_t*>(uprv_malloc(numBytes)); |
143 | 0 | uprv_memcpy(kSerializedStemTrie, result.getBuffer(), numBytes); |
144 | 0 | } |
145 | | |
146 | | |
147 | 0 | inline void appendMultiple(UnicodeString& sb, UChar32 cp, int32_t count) { |
148 | 0 | for (int i = 0; i < count; i++) { |
149 | 0 | sb.append(cp); |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | |
154 | 0 | #define CHECK_NULL(seen, field, status) (void)(seen); /* for auto-format line wrapping */ \ |
155 | 0 | UPRV_BLOCK_MACRO_BEGIN { \ |
156 | 0 | if ((seen).field) { \ |
157 | 0 | (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \ |
158 | 0 | return STATE_NULL; \ |
159 | 0 | } \ |
160 | 0 | (seen).field = true; \ |
161 | 0 | } UPRV_BLOCK_MACRO_END |
162 | | |
163 | | |
164 | | } // anonymous namespace |
165 | | |
166 | | |
167 | 0 | Notation stem_to_object::notation(skeleton::StemEnum stem) { |
168 | 0 | switch (stem) { |
169 | 0 | case STEM_COMPACT_SHORT: |
170 | 0 | return Notation::compactShort(); |
171 | 0 | case STEM_COMPACT_LONG: |
172 | 0 | return Notation::compactLong(); |
173 | 0 | case STEM_SCIENTIFIC: |
174 | 0 | return Notation::scientific(); |
175 | 0 | case STEM_ENGINEERING: |
176 | 0 | return Notation::engineering(); |
177 | 0 | case STEM_NOTATION_SIMPLE: |
178 | 0 | return Notation::simple(); |
179 | 0 | default: |
180 | 0 | UPRV_UNREACHABLE; |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | 0 | MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) { |
185 | 0 | switch (stem) { |
186 | 0 | case STEM_BASE_UNIT: |
187 | 0 | return MeasureUnit(); |
188 | 0 | case STEM_PERCENT: |
189 | 0 | return MeasureUnit::getPercent(); |
190 | 0 | case STEM_PERMILLE: |
191 | 0 | return MeasureUnit::getPermille(); |
192 | 0 | default: |
193 | 0 | UPRV_UNREACHABLE; |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | 0 | Precision stem_to_object::precision(skeleton::StemEnum stem) { |
198 | 0 | switch (stem) { |
199 | 0 | case STEM_PRECISION_INTEGER: |
200 | 0 | return Precision::integer(); |
201 | 0 | case STEM_PRECISION_UNLIMITED: |
202 | 0 | return Precision::unlimited(); |
203 | 0 | case STEM_PRECISION_CURRENCY_STANDARD: |
204 | 0 | return Precision::currency(UCURR_USAGE_STANDARD); |
205 | 0 | case STEM_PRECISION_CURRENCY_CASH: |
206 | 0 | return Precision::currency(UCURR_USAGE_CASH); |
207 | 0 | default: |
208 | 0 | UPRV_UNREACHABLE; |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | 0 | UNumberFormatRoundingMode stem_to_object::roundingMode(skeleton::StemEnum stem) { |
213 | 0 | switch (stem) { |
214 | 0 | case STEM_ROUNDING_MODE_CEILING: |
215 | 0 | return UNUM_ROUND_CEILING; |
216 | 0 | case STEM_ROUNDING_MODE_FLOOR: |
217 | 0 | return UNUM_ROUND_FLOOR; |
218 | 0 | case STEM_ROUNDING_MODE_DOWN: |
219 | 0 | return UNUM_ROUND_DOWN; |
220 | 0 | case STEM_ROUNDING_MODE_UP: |
221 | 0 | return UNUM_ROUND_UP; |
222 | 0 | case STEM_ROUNDING_MODE_HALF_EVEN: |
223 | 0 | return UNUM_ROUND_HALFEVEN; |
224 | 0 | case STEM_ROUNDING_MODE_HALF_ODD: |
225 | 0 | return UNUM_ROUND_HALF_ODD; |
226 | 0 | case STEM_ROUNDING_MODE_HALF_CEILING: |
227 | 0 | return UNUM_ROUND_HALF_CEILING; |
228 | 0 | case STEM_ROUNDING_MODE_HALF_FLOOR: |
229 | 0 | return UNUM_ROUND_HALF_FLOOR; |
230 | 0 | case STEM_ROUNDING_MODE_HALF_DOWN: |
231 | 0 | return UNUM_ROUND_HALFDOWN; |
232 | 0 | case STEM_ROUNDING_MODE_HALF_UP: |
233 | 0 | return UNUM_ROUND_HALFUP; |
234 | 0 | case STEM_ROUNDING_MODE_UNNECESSARY: |
235 | 0 | return UNUM_ROUND_UNNECESSARY; |
236 | 0 | default: |
237 | 0 | UPRV_UNREACHABLE; |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | 0 | UNumberGroupingStrategy stem_to_object::groupingStrategy(skeleton::StemEnum stem) { |
242 | 0 | switch (stem) { |
243 | 0 | case STEM_GROUP_OFF: |
244 | 0 | return UNUM_GROUPING_OFF; |
245 | 0 | case STEM_GROUP_MIN2: |
246 | 0 | return UNUM_GROUPING_MIN2; |
247 | 0 | case STEM_GROUP_AUTO: |
248 | 0 | return UNUM_GROUPING_AUTO; |
249 | 0 | case STEM_GROUP_ON_ALIGNED: |
250 | 0 | return UNUM_GROUPING_ON_ALIGNED; |
251 | 0 | case STEM_GROUP_THOUSANDS: |
252 | 0 | return UNUM_GROUPING_THOUSANDS; |
253 | 0 | default: |
254 | 0 | return UNUM_GROUPING_COUNT; // for objects, throw; for enums, return COUNT |
255 | 0 | } |
256 | 0 | } |
257 | | |
258 | 0 | UNumberUnitWidth stem_to_object::unitWidth(skeleton::StemEnum stem) { |
259 | 0 | switch (stem) { |
260 | 0 | case STEM_UNIT_WIDTH_NARROW: |
261 | 0 | return UNUM_UNIT_WIDTH_NARROW; |
262 | 0 | case STEM_UNIT_WIDTH_SHORT: |
263 | 0 | return UNUM_UNIT_WIDTH_SHORT; |
264 | 0 | case STEM_UNIT_WIDTH_FULL_NAME: |
265 | 0 | return UNUM_UNIT_WIDTH_FULL_NAME; |
266 | 0 | case STEM_UNIT_WIDTH_ISO_CODE: |
267 | 0 | return UNUM_UNIT_WIDTH_ISO_CODE; |
268 | 0 | case STEM_UNIT_WIDTH_FORMAL: |
269 | 0 | return UNUM_UNIT_WIDTH_FORMAL; |
270 | 0 | case STEM_UNIT_WIDTH_VARIANT: |
271 | 0 | return UNUM_UNIT_WIDTH_VARIANT; |
272 | 0 | case STEM_UNIT_WIDTH_HIDDEN: |
273 | 0 | return UNUM_UNIT_WIDTH_HIDDEN; |
274 | 0 | default: |
275 | 0 | return UNUM_UNIT_WIDTH_COUNT; // for objects, throw; for enums, return COUNT |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 0 | UNumberSignDisplay stem_to_object::signDisplay(skeleton::StemEnum stem) { |
280 | 0 | switch (stem) { |
281 | 0 | case STEM_SIGN_AUTO: |
282 | 0 | return UNUM_SIGN_AUTO; |
283 | 0 | case STEM_SIGN_ALWAYS: |
284 | 0 | return UNUM_SIGN_ALWAYS; |
285 | 0 | case STEM_SIGN_NEVER: |
286 | 0 | return UNUM_SIGN_NEVER; |
287 | 0 | case STEM_SIGN_ACCOUNTING: |
288 | 0 | return UNUM_SIGN_ACCOUNTING; |
289 | 0 | case STEM_SIGN_ACCOUNTING_ALWAYS: |
290 | 0 | return UNUM_SIGN_ACCOUNTING_ALWAYS; |
291 | 0 | case STEM_SIGN_EXCEPT_ZERO: |
292 | 0 | return UNUM_SIGN_EXCEPT_ZERO; |
293 | 0 | case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO: |
294 | 0 | return UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; |
295 | 0 | case STEM_SIGN_NEGATIVE: |
296 | 0 | return UNUM_SIGN_NEGATIVE; |
297 | 0 | case STEM_SIGN_ACCOUNTING_NEGATIVE: |
298 | 0 | return UNUM_SIGN_ACCOUNTING_NEGATIVE; |
299 | 0 | default: |
300 | 0 | return UNUM_SIGN_COUNT; // for objects, throw; for enums, return COUNT |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | 0 | UNumberDecimalSeparatorDisplay stem_to_object::decimalSeparatorDisplay(skeleton::StemEnum stem) { |
305 | 0 | switch (stem) { |
306 | 0 | case STEM_DECIMAL_AUTO: |
307 | 0 | return UNUM_DECIMAL_SEPARATOR_AUTO; |
308 | 0 | case STEM_DECIMAL_ALWAYS: |
309 | 0 | return UNUM_DECIMAL_SEPARATOR_ALWAYS; |
310 | 0 | default: |
311 | 0 | return UNUM_DECIMAL_SEPARATOR_COUNT; // for objects, throw; for enums, return COUNT |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | |
316 | 0 | void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb) { |
317 | 0 | switch (value) { |
318 | 0 | case UNUM_ROUND_CEILING: |
319 | 0 | sb.append(u"rounding-mode-ceiling", -1); |
320 | 0 | break; |
321 | 0 | case UNUM_ROUND_FLOOR: |
322 | 0 | sb.append(u"rounding-mode-floor", -1); |
323 | 0 | break; |
324 | 0 | case UNUM_ROUND_DOWN: |
325 | 0 | sb.append(u"rounding-mode-down", -1); |
326 | 0 | break; |
327 | 0 | case UNUM_ROUND_UP: |
328 | 0 | sb.append(u"rounding-mode-up", -1); |
329 | 0 | break; |
330 | 0 | case UNUM_ROUND_HALFEVEN: |
331 | 0 | sb.append(u"rounding-mode-half-even", -1); |
332 | 0 | break; |
333 | 0 | case UNUM_ROUND_HALF_ODD: |
334 | 0 | sb.append(u"rounding-mode-half-odd", -1); |
335 | 0 | break; |
336 | 0 | case UNUM_ROUND_HALF_CEILING: |
337 | 0 | sb.append(u"rounding-mode-half-ceiling", -1); |
338 | 0 | break; |
339 | 0 | case UNUM_ROUND_HALF_FLOOR: |
340 | 0 | sb.append(u"rounding-mode-half-floor", -1); |
341 | 0 | break; |
342 | 0 | case UNUM_ROUND_HALFDOWN: |
343 | 0 | sb.append(u"rounding-mode-half-down", -1); |
344 | 0 | break; |
345 | 0 | case UNUM_ROUND_HALFUP: |
346 | 0 | sb.append(u"rounding-mode-half-up", -1); |
347 | 0 | break; |
348 | 0 | case UNUM_ROUND_UNNECESSARY: |
349 | 0 | sb.append(u"rounding-mode-unnecessary", -1); |
350 | 0 | break; |
351 | 0 | default: |
352 | 0 | UPRV_UNREACHABLE; |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | 0 | void enum_to_stem_string::groupingStrategy(UNumberGroupingStrategy value, UnicodeString& sb) { |
357 | 0 | switch (value) { |
358 | 0 | case UNUM_GROUPING_OFF: |
359 | 0 | sb.append(u"group-off", -1); |
360 | 0 | break; |
361 | 0 | case UNUM_GROUPING_MIN2: |
362 | 0 | sb.append(u"group-min2", -1); |
363 | 0 | break; |
364 | 0 | case UNUM_GROUPING_AUTO: |
365 | 0 | sb.append(u"group-auto", -1); |
366 | 0 | break; |
367 | 0 | case UNUM_GROUPING_ON_ALIGNED: |
368 | 0 | sb.append(u"group-on-aligned", -1); |
369 | 0 | break; |
370 | 0 | case UNUM_GROUPING_THOUSANDS: |
371 | 0 | sb.append(u"group-thousands", -1); |
372 | 0 | break; |
373 | 0 | default: |
374 | 0 | UPRV_UNREACHABLE; |
375 | 0 | } |
376 | 0 | } |
377 | | |
378 | 0 | void enum_to_stem_string::unitWidth(UNumberUnitWidth value, UnicodeString& sb) { |
379 | 0 | switch (value) { |
380 | 0 | case UNUM_UNIT_WIDTH_NARROW: |
381 | 0 | sb.append(u"unit-width-narrow", -1); |
382 | 0 | break; |
383 | 0 | case UNUM_UNIT_WIDTH_SHORT: |
384 | 0 | sb.append(u"unit-width-short", -1); |
385 | 0 | break; |
386 | 0 | case UNUM_UNIT_WIDTH_FULL_NAME: |
387 | 0 | sb.append(u"unit-width-full-name", -1); |
388 | 0 | break; |
389 | 0 | case UNUM_UNIT_WIDTH_ISO_CODE: |
390 | 0 | sb.append(u"unit-width-iso-code", -1); |
391 | 0 | break; |
392 | 0 | case UNUM_UNIT_WIDTH_FORMAL: |
393 | 0 | sb.append(u"unit-width-formal", -1); |
394 | 0 | break; |
395 | 0 | case UNUM_UNIT_WIDTH_VARIANT: |
396 | 0 | sb.append(u"unit-width-variant", -1); |
397 | 0 | break; |
398 | 0 | case UNUM_UNIT_WIDTH_HIDDEN: |
399 | 0 | sb.append(u"unit-width-hidden", -1); |
400 | 0 | break; |
401 | 0 | default: |
402 | 0 | UPRV_UNREACHABLE; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | 0 | void enum_to_stem_string::signDisplay(UNumberSignDisplay value, UnicodeString& sb) { |
407 | 0 | switch (value) { |
408 | 0 | case UNUM_SIGN_AUTO: |
409 | 0 | sb.append(u"sign-auto", -1); |
410 | 0 | break; |
411 | 0 | case UNUM_SIGN_ALWAYS: |
412 | 0 | sb.append(u"sign-always", -1); |
413 | 0 | break; |
414 | 0 | case UNUM_SIGN_NEVER: |
415 | 0 | sb.append(u"sign-never", -1); |
416 | 0 | break; |
417 | 0 | case UNUM_SIGN_ACCOUNTING: |
418 | 0 | sb.append(u"sign-accounting", -1); |
419 | 0 | break; |
420 | 0 | case UNUM_SIGN_ACCOUNTING_ALWAYS: |
421 | 0 | sb.append(u"sign-accounting-always", -1); |
422 | 0 | break; |
423 | 0 | case UNUM_SIGN_EXCEPT_ZERO: |
424 | 0 | sb.append(u"sign-except-zero", -1); |
425 | 0 | break; |
426 | 0 | case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO: |
427 | 0 | sb.append(u"sign-accounting-except-zero", -1); |
428 | 0 | break; |
429 | 0 | case UNUM_SIGN_NEGATIVE: |
430 | 0 | sb.append(u"sign-negative", -1); |
431 | 0 | break; |
432 | 0 | case UNUM_SIGN_ACCOUNTING_NEGATIVE: |
433 | 0 | sb.append(u"sign-accounting-negative", -1); |
434 | 0 | break; |
435 | 0 | default: |
436 | 0 | UPRV_UNREACHABLE; |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | void |
441 | 0 | enum_to_stem_string::decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb) { |
442 | 0 | switch (value) { |
443 | 0 | case UNUM_DECIMAL_SEPARATOR_AUTO: |
444 | 0 | sb.append(u"decimal-auto", -1); |
445 | 0 | break; |
446 | 0 | case UNUM_DECIMAL_SEPARATOR_ALWAYS: |
447 | 0 | sb.append(u"decimal-always", -1); |
448 | 0 | break; |
449 | 0 | default: |
450 | 0 | UPRV_UNREACHABLE; |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | | |
455 | | UnlocalizedNumberFormatter skeleton::create( |
456 | 0 | const UnicodeString& skeletonString, UParseError* perror, UErrorCode& status) { |
457 | | |
458 | | // Initialize perror |
459 | 0 | if (perror != nullptr) { |
460 | 0 | perror->line = 0; |
461 | 0 | perror->offset = -1; |
462 | 0 | perror->preContext[0] = 0; |
463 | 0 | perror->postContext[0] = 0; |
464 | 0 | } |
465 | |
|
466 | 0 | umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status); |
467 | 0 | if (U_FAILURE(status)) { |
468 | 0 | return {}; |
469 | 0 | } |
470 | | |
471 | 0 | int32_t errOffset; |
472 | 0 | MacroProps macros = parseSkeleton(skeletonString, errOffset, status); |
473 | 0 | if (U_SUCCESS(status)) { |
474 | 0 | return NumberFormatter::with().macros(macros); |
475 | 0 | } |
476 | | |
477 | 0 | if (perror == nullptr) { |
478 | 0 | return {}; |
479 | 0 | } |
480 | | |
481 | | // Populate the UParseError with the error location |
482 | 0 | perror->offset = errOffset; |
483 | 0 | int32_t contextStart = uprv_max(0, errOffset - U_PARSE_CONTEXT_LEN + 1); |
484 | 0 | int32_t contextEnd = uprv_min(skeletonString.length(), errOffset + U_PARSE_CONTEXT_LEN - 1); |
485 | 0 | skeletonString.extract(contextStart, errOffset - contextStart, perror->preContext, 0); |
486 | 0 | perror->preContext[errOffset - contextStart] = 0; |
487 | 0 | skeletonString.extract(errOffset, contextEnd - errOffset, perror->postContext, 0); |
488 | 0 | perror->postContext[contextEnd - errOffset] = 0; |
489 | 0 | return {}; |
490 | 0 | } |
491 | | |
492 | 0 | UnicodeString skeleton::generate(const MacroProps& macros, UErrorCode& status) { |
493 | 0 | umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status); |
494 | 0 | UnicodeString sb; |
495 | 0 | GeneratorHelpers::generateSkeleton(macros, sb, status); |
496 | 0 | return sb; |
497 | 0 | } |
498 | | |
499 | | MacroProps skeleton::parseSkeleton( |
500 | 0 | const UnicodeString& skeletonString, int32_t& errOffset, UErrorCode& status) { |
501 | 0 | U_ASSERT(U_SUCCESS(status)); |
502 | 0 | U_ASSERT(kSerializedStemTrie != nullptr); |
503 | | |
504 | | // Add a trailing whitespace to the end of the skeleton string to make code cleaner. |
505 | 0 | UnicodeString tempSkeletonString(skeletonString); |
506 | 0 | tempSkeletonString.append(u' '); |
507 | |
|
508 | 0 | SeenMacroProps seen; |
509 | 0 | MacroProps macros; |
510 | 0 | StringSegment segment(tempSkeletonString, false); |
511 | 0 | UCharsTrie stemTrie(kSerializedStemTrie); |
512 | 0 | ParseState stem = STATE_NULL; |
513 | 0 | int32_t offset = 0; |
514 | | |
515 | | // Primary skeleton parse loop: |
516 | 0 | while (offset < segment.length()) { |
517 | 0 | UChar32 cp = segment.codePointAt(offset); |
518 | 0 | bool isTokenSeparator = PatternProps::isWhiteSpace(cp); |
519 | 0 | bool isOptionSeparator = (cp == u'/'); |
520 | |
|
521 | 0 | if (!isTokenSeparator && !isOptionSeparator) { |
522 | | // Non-separator token; consume it. |
523 | 0 | offset += U16_LENGTH(cp); |
524 | 0 | if (stem == STATE_NULL) { |
525 | | // We are currently consuming a stem. |
526 | | // Go to the next state in the stem trie. |
527 | 0 | stemTrie.nextForCodePoint(cp); |
528 | 0 | } |
529 | 0 | continue; |
530 | 0 | } |
531 | | |
532 | | // We are looking at a token or option separator. |
533 | | // If the segment is nonempty, parse it and reset the segment. |
534 | | // Otherwise, make sure it is a valid repeating separator. |
535 | 0 | if (offset != 0) { |
536 | 0 | segment.setLength(offset); |
537 | 0 | if (stem == STATE_NULL) { |
538 | | // The first separator after the start of a token. Parse it as a stem. |
539 | 0 | stem = parseStem(segment, stemTrie, seen, macros, status); |
540 | 0 | stemTrie.reset(); |
541 | 0 | } else { |
542 | | // A separator after the first separator of a token. Parse it as an option. |
543 | 0 | stem = parseOption(stem, segment, macros, status); |
544 | 0 | } |
545 | 0 | segment.resetLength(); |
546 | 0 | if (U_FAILURE(status)) { |
547 | 0 | errOffset = segment.getOffset(); |
548 | 0 | return macros; |
549 | 0 | } |
550 | | |
551 | | // Consume the segment: |
552 | 0 | segment.adjustOffset(offset); |
553 | 0 | offset = 0; |
554 | |
|
555 | 0 | } else if (stem != STATE_NULL) { |
556 | | // A separator ('/' or whitespace) following an option separator ('/') |
557 | | // segment.setLength(U16_LENGTH(cp)); // for error message |
558 | | // throw new SkeletonSyntaxException("Unexpected separator character", segment); |
559 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
560 | 0 | errOffset = segment.getOffset(); |
561 | 0 | return macros; |
562 | |
|
563 | 0 | } else { |
564 | | // Two spaces in a row; this is OK. |
565 | 0 | } |
566 | | |
567 | | // Does the current stem forbid options? |
568 | 0 | if (isOptionSeparator && stem == STATE_NULL) { |
569 | | // segment.setLength(U16_LENGTH(cp)); // for error message |
570 | | // throw new SkeletonSyntaxException("Unexpected option separator", segment); |
571 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
572 | 0 | errOffset = segment.getOffset(); |
573 | 0 | return macros; |
574 | 0 | } |
575 | | |
576 | | // Does the current stem require an option? |
577 | 0 | if (isTokenSeparator && stem != STATE_NULL) { |
578 | 0 | switch (stem) { |
579 | 0 | case STATE_INCREMENT_PRECISION: |
580 | 0 | case STATE_MEASURE_UNIT: |
581 | 0 | case STATE_PER_MEASURE_UNIT: |
582 | 0 | case STATE_IDENTIFIER_UNIT: |
583 | 0 | case STATE_UNIT_USAGE: |
584 | 0 | case STATE_CURRENCY_UNIT: |
585 | 0 | case STATE_INTEGER_WIDTH: |
586 | 0 | case STATE_NUMBERING_SYSTEM: |
587 | 0 | case STATE_SCALE: |
588 | | // segment.setLength(U16_LENGTH(cp)); // for error message |
589 | | // throw new SkeletonSyntaxException("Stem requires an option", segment); |
590 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
591 | 0 | errOffset = segment.getOffset(); |
592 | 0 | return macros; |
593 | 0 | default: |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | stem = STATE_NULL; |
597 | 0 | } |
598 | | |
599 | | // Consume the separator: |
600 | 0 | segment.adjustOffset(U16_LENGTH(cp)); |
601 | 0 | } |
602 | 0 | U_ASSERT(stem == STATE_NULL); |
603 | 0 | return macros; |
604 | 0 | } |
605 | | |
606 | | ParseState |
607 | | skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen, |
608 | 0 | MacroProps& macros, UErrorCode& status) { |
609 | 0 | U_ASSERT(U_SUCCESS(status)); |
610 | | |
611 | | // First check for "blueprint" stems, which start with a "signal char" |
612 | 0 | switch (segment.charAt(0)) { |
613 | 0 | case u'.': |
614 | 0 | CHECK_NULL(seen, precision, status); |
615 | 0 | blueprint_helpers::parseFractionStem(segment, macros, status); |
616 | 0 | return STATE_FRACTION_PRECISION; |
617 | 0 | case u'@': |
618 | 0 | CHECK_NULL(seen, precision, status); |
619 | 0 | blueprint_helpers::parseDigitsStem(segment, macros, status); |
620 | 0 | return STATE_PRECISION; |
621 | 0 | case u'E': |
622 | 0 | CHECK_NULL(seen, notation, status); |
623 | 0 | blueprint_helpers::parseScientificStem(segment, macros, status); |
624 | 0 | return STATE_NULL; |
625 | 0 | case u'0': |
626 | 0 | CHECK_NULL(seen, integerWidth, status); |
627 | 0 | blueprint_helpers::parseIntegerStem(segment, macros, status); |
628 | 0 | return STATE_NULL; |
629 | 0 | default: |
630 | 0 | break; |
631 | 0 | } |
632 | | |
633 | | // Now look at the stemsTrie, which is already be pointing at our stem. |
634 | 0 | UStringTrieResult stemResult = stemTrie.current(); |
635 | |
|
636 | 0 | if (stemResult != USTRINGTRIE_INTERMEDIATE_VALUE && stemResult != USTRINGTRIE_FINAL_VALUE) { |
637 | | // throw new SkeletonSyntaxException("Unknown stem", segment); |
638 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
639 | 0 | return STATE_NULL; |
640 | 0 | } |
641 | | |
642 | 0 | auto stem = static_cast<StemEnum>(stemTrie.getValue()); |
643 | 0 | switch (stem) { |
644 | | |
645 | | // Stems with meaning on their own, not requiring an option: |
646 | | |
647 | 0 | case STEM_COMPACT_SHORT: |
648 | 0 | case STEM_COMPACT_LONG: |
649 | 0 | case STEM_SCIENTIFIC: |
650 | 0 | case STEM_ENGINEERING: |
651 | 0 | case STEM_NOTATION_SIMPLE: |
652 | 0 | CHECK_NULL(seen, notation, status); |
653 | 0 | macros.notation = stem_to_object::notation(stem); |
654 | 0 | switch (stem) { |
655 | 0 | case STEM_SCIENTIFIC: |
656 | 0 | case STEM_ENGINEERING: |
657 | 0 | return STATE_SCIENTIFIC; // allows for scientific options |
658 | 0 | default: |
659 | 0 | return STATE_NULL; |
660 | 0 | } |
661 | | |
662 | 0 | case STEM_BASE_UNIT: |
663 | 0 | case STEM_PERCENT: |
664 | 0 | case STEM_PERMILLE: |
665 | 0 | CHECK_NULL(seen, unit, status); |
666 | 0 | macros.unit = stem_to_object::unit(stem); |
667 | 0 | return STATE_NULL; |
668 | | |
669 | 0 | case STEM_PERCENT_100: |
670 | 0 | CHECK_NULL(seen, scale, status); |
671 | 0 | CHECK_NULL(seen, unit, status); |
672 | 0 | macros.scale = Scale::powerOfTen(2); |
673 | 0 | macros.unit = NoUnit::percent(); |
674 | 0 | return STATE_NULL; |
675 | | |
676 | 0 | case STEM_PRECISION_INTEGER: |
677 | 0 | case STEM_PRECISION_UNLIMITED: |
678 | 0 | case STEM_PRECISION_CURRENCY_STANDARD: |
679 | 0 | case STEM_PRECISION_CURRENCY_CASH: |
680 | 0 | CHECK_NULL(seen, precision, status); |
681 | 0 | macros.precision = stem_to_object::precision(stem); |
682 | 0 | switch (stem) { |
683 | 0 | case STEM_PRECISION_INTEGER: |
684 | 0 | return STATE_FRACTION_PRECISION; // allows for "precision-integer/@##" |
685 | 0 | default: |
686 | 0 | return STATE_PRECISION; |
687 | 0 | } |
688 | | |
689 | 0 | case STEM_ROUNDING_MODE_CEILING: |
690 | 0 | case STEM_ROUNDING_MODE_FLOOR: |
691 | 0 | case STEM_ROUNDING_MODE_DOWN: |
692 | 0 | case STEM_ROUNDING_MODE_UP: |
693 | 0 | case STEM_ROUNDING_MODE_HALF_EVEN: |
694 | 0 | case STEM_ROUNDING_MODE_HALF_ODD: |
695 | 0 | case STEM_ROUNDING_MODE_HALF_CEILING: |
696 | 0 | case STEM_ROUNDING_MODE_HALF_FLOOR: |
697 | 0 | case STEM_ROUNDING_MODE_HALF_DOWN: |
698 | 0 | case STEM_ROUNDING_MODE_HALF_UP: |
699 | 0 | case STEM_ROUNDING_MODE_UNNECESSARY: |
700 | 0 | CHECK_NULL(seen, roundingMode, status); |
701 | 0 | macros.roundingMode = stem_to_object::roundingMode(stem); |
702 | 0 | return STATE_NULL; |
703 | | |
704 | 0 | case STEM_INTEGER_WIDTH_TRUNC: |
705 | 0 | CHECK_NULL(seen, integerWidth, status); |
706 | 0 | macros.integerWidth = IntegerWidth::zeroFillTo(0).truncateAt(0); |
707 | 0 | return STATE_NULL; |
708 | | |
709 | 0 | case STEM_GROUP_OFF: |
710 | 0 | case STEM_GROUP_MIN2: |
711 | 0 | case STEM_GROUP_AUTO: |
712 | 0 | case STEM_GROUP_ON_ALIGNED: |
713 | 0 | case STEM_GROUP_THOUSANDS: |
714 | 0 | CHECK_NULL(seen, grouper, status); |
715 | 0 | macros.grouper = Grouper::forStrategy(stem_to_object::groupingStrategy(stem)); |
716 | 0 | return STATE_NULL; |
717 | | |
718 | 0 | case STEM_LATIN: |
719 | 0 | CHECK_NULL(seen, symbols, status); |
720 | 0 | macros.symbols.setTo(NumberingSystem::createInstanceByName("latn", status)); |
721 | 0 | return STATE_NULL; |
722 | | |
723 | 0 | case STEM_UNIT_WIDTH_NARROW: |
724 | 0 | case STEM_UNIT_WIDTH_SHORT: |
725 | 0 | case STEM_UNIT_WIDTH_FULL_NAME: |
726 | 0 | case STEM_UNIT_WIDTH_ISO_CODE: |
727 | 0 | case STEM_UNIT_WIDTH_FORMAL: |
728 | 0 | case STEM_UNIT_WIDTH_VARIANT: |
729 | 0 | case STEM_UNIT_WIDTH_HIDDEN: |
730 | 0 | CHECK_NULL(seen, unitWidth, status); |
731 | 0 | macros.unitWidth = stem_to_object::unitWidth(stem); |
732 | 0 | return STATE_NULL; |
733 | | |
734 | 0 | case STEM_SIGN_AUTO: |
735 | 0 | case STEM_SIGN_ALWAYS: |
736 | 0 | case STEM_SIGN_NEVER: |
737 | 0 | case STEM_SIGN_ACCOUNTING: |
738 | 0 | case STEM_SIGN_ACCOUNTING_ALWAYS: |
739 | 0 | case STEM_SIGN_EXCEPT_ZERO: |
740 | 0 | case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO: |
741 | 0 | case STEM_SIGN_NEGATIVE: |
742 | 0 | case STEM_SIGN_ACCOUNTING_NEGATIVE: |
743 | 0 | CHECK_NULL(seen, sign, status); |
744 | 0 | macros.sign = stem_to_object::signDisplay(stem); |
745 | 0 | return STATE_NULL; |
746 | | |
747 | 0 | case STEM_DECIMAL_AUTO: |
748 | 0 | case STEM_DECIMAL_ALWAYS: |
749 | 0 | CHECK_NULL(seen, decimal, status); |
750 | 0 | macros.decimal = stem_to_object::decimalSeparatorDisplay(stem); |
751 | 0 | return STATE_NULL; |
752 | | |
753 | | // Stems requiring an option: |
754 | | |
755 | 0 | case STEM_PRECISION_INCREMENT: |
756 | 0 | CHECK_NULL(seen, precision, status); |
757 | 0 | return STATE_INCREMENT_PRECISION; |
758 | | |
759 | 0 | case STEM_MEASURE_UNIT: |
760 | 0 | CHECK_NULL(seen, unit, status); |
761 | 0 | return STATE_MEASURE_UNIT; |
762 | | |
763 | 0 | case STEM_PER_MEASURE_UNIT: |
764 | 0 | CHECK_NULL(seen, perUnit, status); |
765 | 0 | return STATE_PER_MEASURE_UNIT; |
766 | | |
767 | 0 | case STEM_UNIT: |
768 | 0 | CHECK_NULL(seen, unit, status); |
769 | 0 | CHECK_NULL(seen, perUnit, status); |
770 | 0 | return STATE_IDENTIFIER_UNIT; |
771 | | |
772 | 0 | case STEM_UNIT_USAGE: |
773 | 0 | CHECK_NULL(seen, usage, status); |
774 | 0 | return STATE_UNIT_USAGE; |
775 | | |
776 | 0 | case STEM_CURRENCY: |
777 | 0 | CHECK_NULL(seen, unit, status); |
778 | 0 | CHECK_NULL(seen, perUnit, status); |
779 | 0 | return STATE_CURRENCY_UNIT; |
780 | | |
781 | 0 | case STEM_INTEGER_WIDTH: |
782 | 0 | CHECK_NULL(seen, integerWidth, status); |
783 | 0 | return STATE_INTEGER_WIDTH; |
784 | | |
785 | 0 | case STEM_NUMBERING_SYSTEM: |
786 | 0 | CHECK_NULL(seen, symbols, status); |
787 | 0 | return STATE_NUMBERING_SYSTEM; |
788 | | |
789 | 0 | case STEM_SCALE: |
790 | 0 | CHECK_NULL(seen, scale, status); |
791 | 0 | return STATE_SCALE; |
792 | | |
793 | 0 | default: |
794 | 0 | UPRV_UNREACHABLE; |
795 | 0 | } |
796 | 0 | } |
797 | | |
798 | | ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, |
799 | 0 | UErrorCode& status) { |
800 | 0 | U_ASSERT(U_SUCCESS(status)); |
801 | | |
802 | | ///// Required options: ///// |
803 | |
|
804 | 0 | switch (stem) { |
805 | 0 | case STATE_CURRENCY_UNIT: |
806 | 0 | blueprint_helpers::parseCurrencyOption(segment, macros, status); |
807 | 0 | return STATE_NULL; |
808 | 0 | case STATE_MEASURE_UNIT: |
809 | 0 | blueprint_helpers::parseMeasureUnitOption(segment, macros, status); |
810 | 0 | return STATE_NULL; |
811 | 0 | case STATE_PER_MEASURE_UNIT: |
812 | 0 | blueprint_helpers::parseMeasurePerUnitOption(segment, macros, status); |
813 | 0 | return STATE_NULL; |
814 | 0 | case STATE_IDENTIFIER_UNIT: |
815 | 0 | blueprint_helpers::parseIdentifierUnitOption(segment, macros, status); |
816 | 0 | return STATE_NULL; |
817 | 0 | case STATE_UNIT_USAGE: |
818 | 0 | blueprint_helpers::parseUnitUsageOption(segment, macros, status); |
819 | 0 | return STATE_NULL; |
820 | 0 | case STATE_INCREMENT_PRECISION: |
821 | 0 | blueprint_helpers::parseIncrementOption(segment, macros, status); |
822 | 0 | return STATE_PRECISION; |
823 | 0 | case STATE_INTEGER_WIDTH: |
824 | 0 | blueprint_helpers::parseIntegerWidthOption(segment, macros, status); |
825 | 0 | return STATE_NULL; |
826 | 0 | case STATE_NUMBERING_SYSTEM: |
827 | 0 | blueprint_helpers::parseNumberingSystemOption(segment, macros, status); |
828 | 0 | return STATE_NULL; |
829 | 0 | case STATE_SCALE: |
830 | 0 | blueprint_helpers::parseScaleOption(segment, macros, status); |
831 | 0 | return STATE_NULL; |
832 | 0 | default: |
833 | 0 | break; |
834 | 0 | } |
835 | | |
836 | | ///// Non-required options: ///// |
837 | | |
838 | | // Scientific options |
839 | 0 | switch (stem) { |
840 | 0 | case STATE_SCIENTIFIC: |
841 | 0 | if (blueprint_helpers::parseExponentWidthOption(segment, macros, status)) { |
842 | 0 | return STATE_SCIENTIFIC; |
843 | 0 | } |
844 | 0 | if (U_FAILURE(status)) { |
845 | 0 | return {}; |
846 | 0 | } |
847 | 0 | if (blueprint_helpers::parseExponentSignOption(segment, macros, status)) { |
848 | 0 | return STATE_SCIENTIFIC; |
849 | 0 | } |
850 | 0 | if (U_FAILURE(status)) { |
851 | 0 | return {}; |
852 | 0 | } |
853 | 0 | break; |
854 | 0 | default: |
855 | 0 | break; |
856 | 0 | } |
857 | | |
858 | | // Frac-sig option |
859 | 0 | switch (stem) { |
860 | 0 | case STATE_FRACTION_PRECISION: |
861 | 0 | if (blueprint_helpers::parseFracSigOption(segment, macros, status)) { |
862 | 0 | return STATE_PRECISION; |
863 | 0 | } |
864 | 0 | if (U_FAILURE(status)) { |
865 | 0 | return {}; |
866 | 0 | } |
867 | | // If the fracSig option was not found, try normal precision options. |
868 | 0 | stem = STATE_PRECISION; |
869 | 0 | break; |
870 | 0 | default: |
871 | 0 | break; |
872 | 0 | } |
873 | | |
874 | | // Trailing zeros option |
875 | 0 | switch (stem) { |
876 | 0 | case STATE_PRECISION: |
877 | 0 | if (blueprint_helpers::parseTrailingZeroOption(segment, macros, status)) { |
878 | 0 | return STATE_NULL; |
879 | 0 | } |
880 | 0 | if (U_FAILURE(status)) { |
881 | 0 | return {}; |
882 | 0 | } |
883 | 0 | break; |
884 | 0 | default: |
885 | 0 | break; |
886 | 0 | } |
887 | | |
888 | | // Unknown option |
889 | | // throw new SkeletonSyntaxException("Invalid option", segment); |
890 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
891 | 0 | return STATE_NULL; |
892 | 0 | } |
893 | | |
894 | 0 | void GeneratorHelpers::generateSkeleton(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
895 | 0 | if (U_FAILURE(status)) { return; } |
896 | | |
897 | | // Supported options |
898 | 0 | if (GeneratorHelpers::notation(macros, sb, status)) { |
899 | 0 | sb.append(u' '); |
900 | 0 | } |
901 | 0 | if (U_FAILURE(status)) { return; } |
902 | 0 | if (GeneratorHelpers::unit(macros, sb, status)) { |
903 | 0 | sb.append(u' '); |
904 | 0 | } |
905 | 0 | if (U_FAILURE(status)) { return; } |
906 | 0 | if (GeneratorHelpers::usage(macros, sb, status)) { |
907 | 0 | sb.append(u' '); |
908 | 0 | } |
909 | 0 | if (U_FAILURE(status)) { return; } |
910 | 0 | if (GeneratorHelpers::precision(macros, sb, status)) { |
911 | 0 | sb.append(u' '); |
912 | 0 | } |
913 | 0 | if (U_FAILURE(status)) { return; } |
914 | 0 | if (GeneratorHelpers::roundingMode(macros, sb, status)) { |
915 | 0 | sb.append(u' '); |
916 | 0 | } |
917 | 0 | if (U_FAILURE(status)) { return; } |
918 | 0 | if (GeneratorHelpers::grouping(macros, sb, status)) { |
919 | 0 | sb.append(u' '); |
920 | 0 | } |
921 | 0 | if (U_FAILURE(status)) { return; } |
922 | 0 | if (GeneratorHelpers::integerWidth(macros, sb, status)) { |
923 | 0 | sb.append(u' '); |
924 | 0 | } |
925 | 0 | if (U_FAILURE(status)) { return; } |
926 | 0 | if (GeneratorHelpers::symbols(macros, sb, status)) { |
927 | 0 | sb.append(u' '); |
928 | 0 | } |
929 | 0 | if (U_FAILURE(status)) { return; } |
930 | 0 | if (GeneratorHelpers::unitWidth(macros, sb, status)) { |
931 | 0 | sb.append(u' '); |
932 | 0 | } |
933 | 0 | if (U_FAILURE(status)) { return; } |
934 | 0 | if (GeneratorHelpers::sign(macros, sb, status)) { |
935 | 0 | sb.append(u' '); |
936 | 0 | } |
937 | 0 | if (U_FAILURE(status)) { return; } |
938 | 0 | if (GeneratorHelpers::decimal(macros, sb, status)) { |
939 | 0 | sb.append(u' '); |
940 | 0 | } |
941 | 0 | if (U_FAILURE(status)) { return; } |
942 | 0 | if (GeneratorHelpers::scale(macros, sb, status)) { |
943 | 0 | sb.append(u' '); |
944 | 0 | } |
945 | 0 | if (U_FAILURE(status)) { return; } |
946 | | |
947 | | // Unsupported options |
948 | 0 | if (!macros.padder.isBogus()) { |
949 | 0 | status = U_UNSUPPORTED_ERROR; |
950 | 0 | return; |
951 | 0 | } |
952 | 0 | if (macros.unitDisplayCase.isSet()) { |
953 | 0 | status = U_UNSUPPORTED_ERROR; |
954 | 0 | return; |
955 | 0 | } |
956 | 0 | if (macros.affixProvider != nullptr) { |
957 | 0 | status = U_UNSUPPORTED_ERROR; |
958 | 0 | return; |
959 | 0 | } |
960 | 0 | if (macros.rules != nullptr) { |
961 | 0 | status = U_UNSUPPORTED_ERROR; |
962 | 0 | return; |
963 | 0 | } |
964 | | |
965 | | // Remove the trailing space |
966 | 0 | if (sb.length() > 0) { |
967 | 0 | sb.truncate(sb.length() - 1); |
968 | 0 | } |
969 | 0 | } |
970 | | |
971 | | |
972 | | bool blueprint_helpers::parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, |
973 | 0 | UErrorCode&) { |
974 | 0 | if (!isWildcardChar(segment.charAt(0))) { |
975 | 0 | return false; |
976 | 0 | } |
977 | 0 | int32_t offset = 1; |
978 | 0 | int32_t minExp = 0; |
979 | 0 | for (; offset < segment.length(); offset++) { |
980 | 0 | if (segment.charAt(offset) == u'e') { |
981 | 0 | minExp++; |
982 | 0 | } else { |
983 | 0 | break; |
984 | 0 | } |
985 | 0 | } |
986 | 0 | if (offset < segment.length()) { |
987 | 0 | return false; |
988 | 0 | } |
989 | | // Use the public APIs to enforce bounds checking |
990 | 0 | macros.notation = static_cast<ScientificNotation&>(macros.notation).withMinExponentDigits(minExp); |
991 | 0 | return true; |
992 | 0 | } |
993 | | |
994 | | void |
995 | 0 | blueprint_helpers::generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode&) { |
996 | 0 | sb.append(kWildcardChar); |
997 | 0 | appendMultiple(sb, u'e', minExponentDigits); |
998 | 0 | } |
999 | | |
1000 | | bool |
1001 | 0 | blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { |
1002 | | // Get the sign display type out of the CharsTrie data structure. |
1003 | 0 | UCharsTrie tempStemTrie(kSerializedStemTrie); |
1004 | 0 | UStringTrieResult result = tempStemTrie.next( |
1005 | 0 | segment.toTempUnicodeString().getBuffer(), |
1006 | 0 | segment.length()); |
1007 | 0 | if (result != USTRINGTRIE_INTERMEDIATE_VALUE && result != USTRINGTRIE_FINAL_VALUE) { |
1008 | 0 | return false; |
1009 | 0 | } |
1010 | 0 | auto sign = stem_to_object::signDisplay(static_cast<StemEnum>(tempStemTrie.getValue())); |
1011 | 0 | if (sign == UNUM_SIGN_COUNT) { |
1012 | 0 | return false; |
1013 | 0 | } |
1014 | 0 | macros.notation = static_cast<ScientificNotation&>(macros.notation).withExponentSignDisplay(sign); |
1015 | 0 | return true; |
1016 | 0 | } |
1017 | | |
1018 | | void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros, |
1019 | 0 | UErrorCode& status) { |
1020 | | // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us |
1021 | 0 | if (segment.length() != 3) { |
1022 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1023 | 0 | return; |
1024 | 0 | } |
1025 | 0 | const UChar* currencyCode = segment.toTempUnicodeString().getBuffer(); |
1026 | 0 | UErrorCode localStatus = U_ZERO_ERROR; |
1027 | 0 | CurrencyUnit currency(currencyCode, localStatus); |
1028 | 0 | if (U_FAILURE(localStatus)) { |
1029 | | // Not 3 ascii chars |
1030 | | // throw new SkeletonSyntaxException("Invalid currency", segment); |
1031 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1032 | 0 | return; |
1033 | 0 | } |
1034 | | // Slicing is OK |
1035 | 0 | macros.unit = currency; // NOLINT |
1036 | 0 | } |
1037 | | |
1038 | | void |
1039 | 0 | blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode&) { |
1040 | 0 | sb.append(currency.getISOCurrency(), -1); |
1041 | 0 | } |
1042 | | |
1043 | | void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, |
1044 | 0 | UErrorCode& status) { |
1045 | 0 | U_ASSERT(U_SUCCESS(status)); |
1046 | 0 | const UnicodeString stemString = segment.toTempUnicodeString(); |
1047 | | |
1048 | | // NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric) |
1049 | | // http://unicode.org/reports/tr35/#Validity_Data |
1050 | 0 | int firstHyphen = 0; |
1051 | 0 | while (firstHyphen < stemString.length() && stemString.charAt(firstHyphen) != '-') { |
1052 | 0 | firstHyphen++; |
1053 | 0 | } |
1054 | 0 | if (firstHyphen == stemString.length()) { |
1055 | | // throw new SkeletonSyntaxException("Invalid measure unit option", segment); |
1056 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1057 | 0 | return; |
1058 | 0 | } |
1059 | | |
1060 | | // Need to do char <-> UChar conversion... |
1061 | 0 | CharString type; |
1062 | 0 | SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status); |
1063 | 0 | CharString subType; |
1064 | 0 | SKELETON_UCHAR_TO_CHAR(subType, stemString, firstHyphen + 1, stemString.length(), status); |
1065 | | |
1066 | | // Note: the largest type as of this writing (Aug 2020) is "volume", which has 33 units. |
1067 | 0 | static constexpr int32_t CAPACITY = 40; |
1068 | 0 | MeasureUnit units[CAPACITY]; |
1069 | 0 | UErrorCode localStatus = U_ZERO_ERROR; |
1070 | 0 | int32_t numUnits = MeasureUnit::getAvailable(type.data(), units, CAPACITY, localStatus); |
1071 | 0 | if (U_FAILURE(localStatus)) { |
1072 | | // More than 30 units in this type? |
1073 | 0 | status = U_INTERNAL_PROGRAM_ERROR; |
1074 | 0 | return; |
1075 | 0 | } |
1076 | 0 | for (int32_t i = 0; i < numUnits; i++) { |
1077 | 0 | auto& unit = units[i]; |
1078 | 0 | if (uprv_strcmp(subType.data(), unit.getSubtype()) == 0) { |
1079 | 0 | macros.unit = unit; |
1080 | 0 | return; |
1081 | 0 | } |
1082 | 0 | } |
1083 | | |
1084 | | // throw new SkeletonSyntaxException("Unknown measure unit", segment); |
1085 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1086 | 0 | } |
1087 | | |
1088 | | void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, |
1089 | 0 | UErrorCode& status) { |
1090 | | // A little bit of a hack: save the current unit (numerator), call the main measure unit |
1091 | | // parsing code, put back the numerator unit, and put the new unit into per-unit. |
1092 | 0 | MeasureUnit numerator = macros.unit; |
1093 | 0 | parseMeasureUnitOption(segment, macros, status); |
1094 | 0 | if (U_FAILURE(status)) { return; } |
1095 | 0 | macros.perUnit = macros.unit; |
1096 | 0 | macros.unit = numerator; |
1097 | 0 | } |
1098 | | |
1099 | | void blueprint_helpers::parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros, |
1100 | 0 | UErrorCode& status) { |
1101 | | // Need to do char <-> UChar conversion... |
1102 | 0 | U_ASSERT(U_SUCCESS(status)); |
1103 | 0 | CharString buffer; |
1104 | 0 | SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); |
1105 | | |
1106 | 0 | ErrorCode internalStatus; |
1107 | 0 | macros.unit = MeasureUnit::forIdentifier(buffer.toStringPiece(), internalStatus); |
1108 | 0 | if (internalStatus.isFailure()) { |
1109 | | // throw new SkeletonSyntaxException("Invalid core unit identifier", segment, e); |
1110 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1111 | 0 | return; |
1112 | 0 | } |
1113 | 0 | } |
1114 | | |
1115 | | void blueprint_helpers::parseUnitUsageOption(const StringSegment &segment, MacroProps ¯os, |
1116 | 0 | UErrorCode &status) { |
1117 | | // Need to do char <-> UChar conversion... |
1118 | 0 | U_ASSERT(U_SUCCESS(status)); |
1119 | 0 | CharString buffer; |
1120 | 0 | SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); |
1121 | 0 | macros.usage.set(buffer.toStringPiece()); |
1122 | | // We do not do any validation of the usage string: it depends on the |
1123 | | // unitPreferenceData in the units resources. |
1124 | 0 | } |
1125 | | |
1126 | | void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros, |
1127 | 0 | UErrorCode& status) { |
1128 | 0 | U_ASSERT(segment.charAt(0) == u'.'); |
1129 | 0 | int32_t offset = 1; |
1130 | 0 | int32_t minFrac = 0; |
1131 | 0 | int32_t maxFrac; |
1132 | 0 | for (; offset < segment.length(); offset++) { |
1133 | 0 | if (segment.charAt(offset) == u'0') { |
1134 | 0 | minFrac++; |
1135 | 0 | } else { |
1136 | 0 | break; |
1137 | 0 | } |
1138 | 0 | } |
1139 | 0 | if (offset < segment.length()) { |
1140 | 0 | if (isWildcardChar(segment.charAt(offset))) { |
1141 | 0 | maxFrac = -1; |
1142 | 0 | offset++; |
1143 | 0 | } else { |
1144 | 0 | maxFrac = minFrac; |
1145 | 0 | for (; offset < segment.length(); offset++) { |
1146 | 0 | if (segment.charAt(offset) == u'#') { |
1147 | 0 | maxFrac++; |
1148 | 0 | } else { |
1149 | 0 | break; |
1150 | 0 | } |
1151 | 0 | } |
1152 | 0 | } |
1153 | 0 | } else { |
1154 | 0 | maxFrac = minFrac; |
1155 | 0 | } |
1156 | 0 | if (offset < segment.length()) { |
1157 | | // throw new SkeletonSyntaxException("Invalid fraction stem", segment); |
1158 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1159 | 0 | return; |
1160 | 0 | } |
1161 | | // Use the public APIs to enforce bounds checking |
1162 | 0 | if (maxFrac == -1) { |
1163 | 0 | if (minFrac == 0) { |
1164 | 0 | macros.precision = Precision::unlimited(); |
1165 | 0 | } else { |
1166 | 0 | macros.precision = Precision::minFraction(minFrac); |
1167 | 0 | } |
1168 | 0 | } else { |
1169 | 0 | macros.precision = Precision::minMaxFraction(minFrac, maxFrac); |
1170 | 0 | } |
1171 | 0 | } |
1172 | | |
1173 | | void |
1174 | 0 | blueprint_helpers::generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode&) { |
1175 | 0 | if (minFrac == 0 && maxFrac == 0) { |
1176 | 0 | sb.append(u"precision-integer", -1); |
1177 | 0 | return; |
1178 | 0 | } |
1179 | 0 | sb.append(u'.'); |
1180 | 0 | appendMultiple(sb, u'0', minFrac); |
1181 | 0 | if (maxFrac == -1) { |
1182 | 0 | sb.append(kWildcardChar); |
1183 | 0 | } else { |
1184 | 0 | appendMultiple(sb, u'#', maxFrac - minFrac); |
1185 | 0 | } |
1186 | 0 | } |
1187 | | |
1188 | | void |
1189 | 0 | blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { |
1190 | 0 | U_ASSERT(segment.charAt(0) == u'@'); |
1191 | 0 | int32_t offset = 0; |
1192 | 0 | int32_t minSig = 0; |
1193 | 0 | int32_t maxSig; |
1194 | 0 | for (; offset < segment.length(); offset++) { |
1195 | 0 | if (segment.charAt(offset) == u'@') { |
1196 | 0 | minSig++; |
1197 | 0 | } else { |
1198 | 0 | break; |
1199 | 0 | } |
1200 | 0 | } |
1201 | 0 | if (offset < segment.length()) { |
1202 | 0 | if (isWildcardChar(segment.charAt(offset))) { |
1203 | 0 | maxSig = -1; |
1204 | 0 | offset++; |
1205 | 0 | } else { |
1206 | 0 | maxSig = minSig; |
1207 | 0 | for (; offset < segment.length(); offset++) { |
1208 | 0 | if (segment.charAt(offset) == u'#') { |
1209 | 0 | maxSig++; |
1210 | 0 | } else { |
1211 | 0 | break; |
1212 | 0 | } |
1213 | 0 | } |
1214 | 0 | } |
1215 | 0 | } else { |
1216 | 0 | maxSig = minSig; |
1217 | 0 | } |
1218 | 0 | if (offset < segment.length()) { |
1219 | | // throw new SkeletonSyntaxException("Invalid significant digits stem", segment); |
1220 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1221 | 0 | return; |
1222 | 0 | } |
1223 | | // Use the public APIs to enforce bounds checking |
1224 | 0 | if (maxSig == -1) { |
1225 | 0 | macros.precision = Precision::minSignificantDigits(minSig); |
1226 | 0 | } else { |
1227 | 0 | macros.precision = Precision::minMaxSignificantDigits(minSig, maxSig); |
1228 | 0 | } |
1229 | 0 | } |
1230 | | |
1231 | | void |
1232 | 0 | blueprint_helpers::generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode&) { |
1233 | 0 | appendMultiple(sb, u'@', minSig); |
1234 | 0 | if (maxSig == -1) { |
1235 | 0 | sb.append(kWildcardChar); |
1236 | 0 | } else { |
1237 | 0 | appendMultiple(sb, u'#', maxSig - minSig); |
1238 | 0 | } |
1239 | 0 | } |
1240 | | |
1241 | 0 | void blueprint_helpers::parseScientificStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { |
1242 | 0 | U_ASSERT(segment.charAt(0) == u'E'); |
1243 | 0 | { |
1244 | 0 | int32_t offset = 1; |
1245 | 0 | if (segment.length() == offset) { |
1246 | 0 | goto fail; |
1247 | 0 | } |
1248 | 0 | bool isEngineering = false; |
1249 | 0 | if (segment.charAt(offset) == u'E') { |
1250 | 0 | isEngineering = true; |
1251 | 0 | offset++; |
1252 | 0 | if (segment.length() == offset) { |
1253 | 0 | goto fail; |
1254 | 0 | } |
1255 | 0 | } |
1256 | 0 | UNumberSignDisplay signDisplay = UNUM_SIGN_AUTO; |
1257 | 0 | if (segment.charAt(offset) == u'+') { |
1258 | 0 | offset++; |
1259 | 0 | if (segment.length() == offset) { |
1260 | 0 | goto fail; |
1261 | 0 | } |
1262 | 0 | if (segment.charAt(offset) == u'!') { |
1263 | 0 | signDisplay = UNUM_SIGN_ALWAYS; |
1264 | 0 | } else if (segment.charAt(offset) == u'?') { |
1265 | 0 | signDisplay = UNUM_SIGN_EXCEPT_ZERO; |
1266 | 0 | } else { |
1267 | | // NOTE: Other sign displays are not included because they aren't useful in this context |
1268 | 0 | goto fail; |
1269 | 0 | } |
1270 | 0 | offset++; |
1271 | 0 | if (segment.length() == offset) { |
1272 | 0 | goto fail; |
1273 | 0 | } |
1274 | 0 | } |
1275 | 0 | int32_t minDigits = 0; |
1276 | 0 | for (; offset < segment.length(); offset++) { |
1277 | 0 | if (segment.charAt(offset) != u'0') { |
1278 | 0 | goto fail; |
1279 | 0 | } |
1280 | 0 | minDigits++; |
1281 | 0 | } |
1282 | 0 | macros.notation = (isEngineering ? Notation::engineering() : Notation::scientific()) |
1283 | 0 | .withExponentSignDisplay(signDisplay) |
1284 | 0 | .withMinExponentDigits(minDigits); |
1285 | 0 | return; |
1286 | 0 | } |
1287 | 0 | fail: void(); |
1288 | | // throw new SkeletonSyntaxException("Invalid scientific stem", segment); |
1289 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1290 | 0 | return; |
1291 | 0 | } |
1292 | | |
1293 | 0 | void blueprint_helpers::parseIntegerStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { |
1294 | 0 | U_ASSERT(segment.charAt(0) == u'0'); |
1295 | 0 | int32_t offset = 1; |
1296 | 0 | for (; offset < segment.length(); offset++) { |
1297 | 0 | if (segment.charAt(offset) != u'0') { |
1298 | 0 | offset--; |
1299 | 0 | break; |
1300 | 0 | } |
1301 | 0 | } |
1302 | 0 | if (offset < segment.length()) { |
1303 | | // throw new SkeletonSyntaxException("Invalid integer stem", segment); |
1304 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1305 | 0 | return; |
1306 | 0 | } |
1307 | 0 | macros.integerWidth = IntegerWidth::zeroFillTo(offset); |
1308 | 0 | return; |
1309 | 0 | } |
1310 | | |
1311 | | bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroProps& macros, |
1312 | 0 | UErrorCode& status) { |
1313 | 0 | if (segment.charAt(0) != u'@') { |
1314 | 0 | return false; |
1315 | 0 | } |
1316 | 0 | int offset = 0; |
1317 | 0 | int minSig = 0; |
1318 | 0 | int maxSig; |
1319 | 0 | for (; offset < segment.length(); offset++) { |
1320 | 0 | if (segment.charAt(offset) == u'@') { |
1321 | 0 | minSig++; |
1322 | 0 | } else { |
1323 | 0 | break; |
1324 | 0 | } |
1325 | 0 | } |
1326 | 0 | if (offset < segment.length()) { |
1327 | 0 | if (isWildcardChar(segment.charAt(offset))) { |
1328 | | // @+, @@+, @@@+ |
1329 | 0 | maxSig = -1; |
1330 | 0 | offset++; |
1331 | 0 | } else { |
1332 | | // @#, @##, @### |
1333 | | // @@#, @@##, @@@# |
1334 | 0 | maxSig = minSig; |
1335 | 0 | for (; offset < segment.length(); offset++) { |
1336 | 0 | if (segment.charAt(offset) == u'#') { |
1337 | 0 | maxSig++; |
1338 | 0 | } else { |
1339 | 0 | break; |
1340 | 0 | } |
1341 | 0 | } |
1342 | 0 | } |
1343 | 0 | } else { |
1344 | | // @, @@, @@@ |
1345 | 0 | maxSig = minSig; |
1346 | 0 | } |
1347 | 0 | UNumberRoundingPriority priority; |
1348 | 0 | if (offset < segment.length()) { |
1349 | 0 | if (maxSig == -1) { |
1350 | | // The wildcard character is not allowed with the priority annotation |
1351 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1352 | 0 | return false; |
1353 | 0 | } |
1354 | 0 | if (segment.codePointAt(offset) == u'r') { |
1355 | 0 | priority = UNUM_ROUNDING_PRIORITY_RELAXED; |
1356 | 0 | offset++; |
1357 | 0 | } else if (segment.codePointAt(offset) == u's') { |
1358 | 0 | priority = UNUM_ROUNDING_PRIORITY_STRICT; |
1359 | 0 | offset++; |
1360 | 0 | } else { |
1361 | 0 | U_ASSERT(offset < segment.length()); |
1362 | 0 | } |
1363 | 0 | if (offset < segment.length()) { |
1364 | | // Invalid digits option for fraction rounder |
1365 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1366 | 0 | return false; |
1367 | 0 | } |
1368 | 0 | } else if (maxSig == -1) { |
1369 | | // withMinDigits |
1370 | 0 | maxSig = minSig; |
1371 | 0 | minSig = 1; |
1372 | 0 | priority = UNUM_ROUNDING_PRIORITY_RELAXED; |
1373 | 0 | } else if (minSig == 1) { |
1374 | | // withMaxDigits |
1375 | 0 | priority = UNUM_ROUNDING_PRIORITY_STRICT; |
1376 | 0 | } else { |
1377 | | // Digits options with both min and max sig require the priority option |
1378 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1379 | 0 | return false; |
1380 | 0 | } |
1381 | | |
1382 | 0 | auto& oldPrecision = static_cast<const FractionPrecision&>(macros.precision); |
1383 | 0 | macros.precision = oldPrecision.withSignificantDigits(minSig, maxSig, priority); |
1384 | 0 | return true; |
1385 | 0 | } |
1386 | | |
1387 | 0 | bool blueprint_helpers::parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { |
1388 | 0 | if (segment == u"w") { |
1389 | 0 | macros.precision = macros.precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE); |
1390 | 0 | return true; |
1391 | 0 | } |
1392 | 0 | return false; |
1393 | 0 | } |
1394 | | |
1395 | | void blueprint_helpers::parseIncrementOption(const StringSegment &segment, MacroProps ¯os, |
1396 | 0 | UErrorCode &status) { |
1397 | 0 | number::impl::parseIncrementOption(segment, macros.precision, status); |
1398 | 0 | } |
1399 | | |
1400 | | void blueprint_helpers::generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb, |
1401 | 0 | UErrorCode&) { |
1402 | | // Utilize DecimalQuantity/double_conversion to format this for us. |
1403 | 0 | DecimalQuantity dq; |
1404 | 0 | dq.setToDouble(increment); |
1405 | 0 | dq.roundToInfinity(); |
1406 | 0 | sb.append(dq.toPlainString()); |
1407 | | |
1408 | | // We might need to append extra trailing zeros for min fraction... |
1409 | 0 | if (trailingZeros > 0) { |
1410 | 0 | appendMultiple(sb, u'0', trailingZeros); |
1411 | 0 | } |
1412 | 0 | } |
1413 | | |
1414 | | void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, |
1415 | 0 | UErrorCode& status) { |
1416 | 0 | int32_t offset = 0; |
1417 | 0 | int32_t minInt = 0; |
1418 | 0 | int32_t maxInt; |
1419 | 0 | if (isWildcardChar(segment.charAt(0))) { |
1420 | 0 | maxInt = -1; |
1421 | 0 | offset++; |
1422 | 0 | } else { |
1423 | 0 | maxInt = 0; |
1424 | 0 | } |
1425 | 0 | for (; offset < segment.length(); offset++) { |
1426 | 0 | if (maxInt != -1 && segment.charAt(offset) == u'#') { |
1427 | 0 | maxInt++; |
1428 | 0 | } else { |
1429 | 0 | break; |
1430 | 0 | } |
1431 | 0 | } |
1432 | 0 | if (offset < segment.length()) { |
1433 | 0 | for (; offset < segment.length(); offset++) { |
1434 | 0 | if (segment.charAt(offset) == u'0') { |
1435 | 0 | minInt++; |
1436 | 0 | } else { |
1437 | 0 | break; |
1438 | 0 | } |
1439 | 0 | } |
1440 | 0 | } |
1441 | 0 | if (maxInt != -1) { |
1442 | 0 | maxInt += minInt; |
1443 | 0 | } |
1444 | 0 | if (offset < segment.length()) { |
1445 | | // throw new SkeletonSyntaxException("Invalid integer width stem", segment); |
1446 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1447 | 0 | return; |
1448 | 0 | } |
1449 | | // Use the public APIs to enforce bounds checking |
1450 | 0 | if (maxInt == -1) { |
1451 | 0 | macros.integerWidth = IntegerWidth::zeroFillTo(minInt); |
1452 | 0 | } else { |
1453 | 0 | macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); |
1454 | 0 | } |
1455 | 0 | } |
1456 | | |
1457 | | void blueprint_helpers::generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, |
1458 | 0 | UErrorCode&) { |
1459 | 0 | if (maxInt == -1) { |
1460 | 0 | sb.append(kWildcardChar); |
1461 | 0 | } else { |
1462 | 0 | appendMultiple(sb, u'#', maxInt - minInt); |
1463 | 0 | } |
1464 | 0 | appendMultiple(sb, u'0', minInt); |
1465 | 0 | } |
1466 | | |
1467 | | void blueprint_helpers::parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros, |
1468 | 0 | UErrorCode& status) { |
1469 | | // Need to do char <-> UChar conversion... |
1470 | 0 | U_ASSERT(U_SUCCESS(status)); |
1471 | 0 | CharString buffer; |
1472 | 0 | SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); |
1473 | | |
1474 | 0 | NumberingSystem* ns = NumberingSystem::createInstanceByName(buffer.data(), status); |
1475 | 0 | if (ns == nullptr || U_FAILURE(status)) { |
1476 | | // This is a skeleton syntax error; don't bubble up the low-level NumberingSystem error |
1477 | | // throw new SkeletonSyntaxException("Unknown numbering system", segment); |
1478 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1479 | 0 | return; |
1480 | 0 | } |
1481 | 0 | macros.symbols.setTo(ns); |
1482 | 0 | } |
1483 | | |
1484 | | void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb, |
1485 | 0 | UErrorCode&) { |
1486 | | // Need to do char <-> UChar conversion... |
1487 | 0 | sb.append(UnicodeString(ns.getName(), -1, US_INV)); |
1488 | 0 | } |
1489 | | |
1490 | | void blueprint_helpers::parseScaleOption(const StringSegment& segment, MacroProps& macros, |
1491 | 0 | UErrorCode& status) { |
1492 | | // Need to do char <-> UChar conversion... |
1493 | 0 | U_ASSERT(U_SUCCESS(status)); |
1494 | 0 | CharString buffer; |
1495 | 0 | SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); |
1496 | | |
1497 | 0 | LocalPointer<DecNum> decnum(new DecNum(), status); |
1498 | 0 | if (U_FAILURE(status)) { return; } |
1499 | 0 | decnum->setTo({buffer.data(), buffer.length()}, status); |
1500 | 0 | if (U_FAILURE(status)) { |
1501 | | // This is a skeleton syntax error; don't let the low-level decnum error bubble up |
1502 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
1503 | 0 | return; |
1504 | 0 | } |
1505 | | |
1506 | | // NOTE: The constructor will optimize the decnum for us if possible. |
1507 | 0 | macros.scale = {0, decnum.orphan()}; |
1508 | 0 | } |
1509 | | |
1510 | | void blueprint_helpers::generateScaleOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb, |
1511 | 0 | UErrorCode& status) { |
1512 | | // Utilize DecimalQuantity/double_conversion to format this for us. |
1513 | 0 | DecimalQuantity dq; |
1514 | 0 | if (arbitrary != nullptr) { |
1515 | 0 | dq.setToDecNum(*arbitrary, status); |
1516 | 0 | if (U_FAILURE(status)) { return; } |
1517 | 0 | } else { |
1518 | 0 | dq.setToInt(1); |
1519 | 0 | } |
1520 | 0 | dq.adjustMagnitude(magnitude); |
1521 | 0 | dq.roundToInfinity(); |
1522 | 0 | sb.append(dq.toPlainString()); |
1523 | 0 | } |
1524 | | |
1525 | | |
1526 | 0 | bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1527 | 0 | if (macros.notation.fType == Notation::NTN_COMPACT) { |
1528 | 0 | UNumberCompactStyle style = macros.notation.fUnion.compactStyle; |
1529 | 0 | if (style == UNumberCompactStyle::UNUM_LONG) { |
1530 | 0 | sb.append(u"compact-long", -1); |
1531 | 0 | return true; |
1532 | 0 | } else if (style == UNumberCompactStyle::UNUM_SHORT) { |
1533 | 0 | sb.append(u"compact-short", -1); |
1534 | 0 | return true; |
1535 | 0 | } else { |
1536 | | // Compact notation generated from custom data (not supported in skeleton) |
1537 | | // The other compact notations are literals |
1538 | 0 | status = U_UNSUPPORTED_ERROR; |
1539 | 0 | return false; |
1540 | 0 | } |
1541 | 0 | } else if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { |
1542 | 0 | const Notation::ScientificSettings& impl = macros.notation.fUnion.scientific; |
1543 | 0 | if (impl.fEngineeringInterval == 3) { |
1544 | 0 | sb.append(u"engineering", -1); |
1545 | 0 | } else { |
1546 | 0 | sb.append(u"scientific", -1); |
1547 | 0 | } |
1548 | 0 | if (impl.fMinExponentDigits > 1) { |
1549 | 0 | sb.append(u'/'); |
1550 | 0 | blueprint_helpers::generateExponentWidthOption(impl.fMinExponentDigits, sb, status); |
1551 | 0 | if (U_FAILURE(status)) { |
1552 | 0 | return false; |
1553 | 0 | } |
1554 | 0 | } |
1555 | 0 | if (impl.fExponentSignDisplay != UNUM_SIGN_AUTO) { |
1556 | 0 | sb.append(u'/'); |
1557 | 0 | enum_to_stem_string::signDisplay(impl.fExponentSignDisplay, sb); |
1558 | 0 | } |
1559 | 0 | return true; |
1560 | 0 | } else { |
1561 | | // Default value is not shown in normalized form |
1562 | 0 | return false; |
1563 | 0 | } |
1564 | 0 | } |
1565 | | |
1566 | 0 | bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1567 | 0 | MeasureUnit unit = macros.unit; |
1568 | 0 | if (!utils::unitIsBaseUnit(macros.perUnit)) { |
1569 | 0 | if (utils::unitIsCurrency(macros.unit) || utils::unitIsCurrency(macros.perUnit)) { |
1570 | 0 | status = U_UNSUPPORTED_ERROR; |
1571 | 0 | return false; |
1572 | 0 | } |
1573 | 0 | unit = unit.product(macros.perUnit.reciprocal(status), status); |
1574 | 0 | } |
1575 | | |
1576 | 0 | if (utils::unitIsCurrency(unit)) { |
1577 | 0 | sb.append(u"currency/", -1); |
1578 | 0 | CurrencyUnit currency(unit, status); |
1579 | 0 | if (U_FAILURE(status)) { |
1580 | 0 | return false; |
1581 | 0 | } |
1582 | 0 | blueprint_helpers::generateCurrencyOption(currency, sb, status); |
1583 | 0 | return true; |
1584 | 0 | } else if (utils::unitIsBaseUnit(unit)) { |
1585 | | // Default value is not shown in normalized form |
1586 | 0 | return false; |
1587 | 0 | } else if (utils::unitIsPercent(unit)) { |
1588 | 0 | sb.append(u"percent", -1); |
1589 | 0 | return true; |
1590 | 0 | } else if (utils::unitIsPermille(unit)) { |
1591 | 0 | sb.append(u"permille", -1); |
1592 | 0 | return true; |
1593 | 0 | } else { |
1594 | 0 | sb.append(u"unit/", -1); |
1595 | 0 | sb.append(unit.getIdentifier()); |
1596 | 0 | return true; |
1597 | 0 | } |
1598 | 0 | } |
1599 | | |
1600 | 0 | bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& /* status */) { |
1601 | 0 | if (macros.usage.isSet()) { |
1602 | 0 | sb.append(u"usage/", -1); |
1603 | 0 | sb.append(UnicodeString(macros.usage.fValue, -1, US_INV)); |
1604 | 0 | return true; |
1605 | 0 | } |
1606 | 0 | return false; |
1607 | 0 | } |
1608 | | |
1609 | 0 | bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1610 | 0 | if (macros.precision.fType == Precision::RND_NONE) { |
1611 | 0 | sb.append(u"precision-unlimited", -1); |
1612 | 0 | } else if (macros.precision.fType == Precision::RND_FRACTION) { |
1613 | 0 | const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; |
1614 | 0 | blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status); |
1615 | 0 | } else if (macros.precision.fType == Precision::RND_SIGNIFICANT) { |
1616 | 0 | const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; |
1617 | 0 | blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); |
1618 | 0 | } else if (macros.precision.fType == Precision::RND_FRACTION_SIGNIFICANT) { |
1619 | 0 | const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; |
1620 | 0 | blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status); |
1621 | 0 | sb.append(u'/'); |
1622 | 0 | blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); |
1623 | 0 | if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { |
1624 | 0 | sb.append(u'r'); |
1625 | 0 | } else { |
1626 | 0 | sb.append(u's'); |
1627 | 0 | } |
1628 | 0 | } else if (macros.precision.fType == Precision::RND_INCREMENT |
1629 | 0 | || macros.precision.fType == Precision::RND_INCREMENT_ONE |
1630 | 0 | || macros.precision.fType == Precision::RND_INCREMENT_FIVE) { |
1631 | 0 | const Precision::IncrementSettings& impl = macros.precision.fUnion.increment; |
1632 | 0 | sb.append(u"precision-increment/", -1); |
1633 | 0 | blueprint_helpers::generateIncrementOption( |
1634 | 0 | impl.fIncrement, |
1635 | 0 | impl.fMinFrac - impl.fMaxFrac, |
1636 | 0 | sb, |
1637 | 0 | status); |
1638 | 0 | } else if (macros.precision.fType == Precision::RND_CURRENCY) { |
1639 | 0 | UCurrencyUsage usage = macros.precision.fUnion.currencyUsage; |
1640 | 0 | if (usage == UCURR_USAGE_STANDARD) { |
1641 | 0 | sb.append(u"precision-currency-standard", -1); |
1642 | 0 | } else { |
1643 | 0 | sb.append(u"precision-currency-cash", -1); |
1644 | 0 | } |
1645 | 0 | } else { |
1646 | | // Bogus or Error |
1647 | 0 | return false; |
1648 | 0 | } |
1649 | | |
1650 | 0 | if (macros.precision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_HIDE_IF_WHOLE) { |
1651 | 0 | sb.append(u"/w", -1); |
1652 | 0 | } |
1653 | | |
1654 | | // NOTE: Always return true for rounding because the default value depends on other options. |
1655 | 0 | return true; |
1656 | 0 | } |
1657 | | |
1658 | 0 | bool GeneratorHelpers::roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { |
1659 | 0 | if (macros.roundingMode == kDefaultMode) { |
1660 | 0 | return false; // Default |
1661 | 0 | } |
1662 | 0 | enum_to_stem_string::roundingMode(macros.roundingMode, sb); |
1663 | 0 | return true; |
1664 | 0 | } |
1665 | | |
1666 | 0 | bool GeneratorHelpers::grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1667 | 0 | if (macros.grouper.isBogus()) { |
1668 | 0 | return false; // No value |
1669 | 0 | } else if (macros.grouper.fStrategy == UNUM_GROUPING_COUNT) { |
1670 | 0 | status = U_UNSUPPORTED_ERROR; |
1671 | 0 | return false; |
1672 | 0 | } else if (macros.grouper.fStrategy == UNUM_GROUPING_AUTO) { |
1673 | 0 | return false; // Default value |
1674 | 0 | } else { |
1675 | 0 | enum_to_stem_string::groupingStrategy(macros.grouper.fStrategy, sb); |
1676 | 0 | return true; |
1677 | 0 | } |
1678 | 0 | } |
1679 | | |
1680 | 0 | bool GeneratorHelpers::integerWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1681 | 0 | if (macros.integerWidth.fHasError || macros.integerWidth.isBogus() || |
1682 | 0 | macros.integerWidth == IntegerWidth::standard()) { |
1683 | | // Error or Default |
1684 | 0 | return false; |
1685 | 0 | } |
1686 | 0 | const auto& minMaxInt = macros.integerWidth.fUnion.minMaxInt; |
1687 | 0 | if (minMaxInt.fMinInt == 0 && minMaxInt.fMaxInt == 0) { |
1688 | 0 | sb.append(u"integer-width-trunc", -1); |
1689 | 0 | return true; |
1690 | 0 | } |
1691 | 0 | sb.append(u"integer-width/", -1); |
1692 | 0 | blueprint_helpers::generateIntegerWidthOption( |
1693 | 0 | minMaxInt.fMinInt, |
1694 | 0 | minMaxInt.fMaxInt, |
1695 | 0 | sb, |
1696 | 0 | status); |
1697 | 0 | return true; |
1698 | 0 | } |
1699 | | |
1700 | 0 | bool GeneratorHelpers::symbols(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1701 | 0 | if (macros.symbols.isNumberingSystem()) { |
1702 | 0 | const NumberingSystem& ns = *macros.symbols.getNumberingSystem(); |
1703 | 0 | if (uprv_strcmp(ns.getName(), "latn") == 0) { |
1704 | 0 | sb.append(u"latin", -1); |
1705 | 0 | } else { |
1706 | 0 | sb.append(u"numbering-system/", -1); |
1707 | 0 | blueprint_helpers::generateNumberingSystemOption(ns, sb, status); |
1708 | 0 | } |
1709 | 0 | return true; |
1710 | 0 | } else if (macros.symbols.isDecimalFormatSymbols()) { |
1711 | 0 | status = U_UNSUPPORTED_ERROR; |
1712 | 0 | return false; |
1713 | 0 | } else { |
1714 | | // No custom symbols |
1715 | 0 | return false; |
1716 | 0 | } |
1717 | 0 | } |
1718 | | |
1719 | 0 | bool GeneratorHelpers::unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { |
1720 | 0 | if (macros.unitWidth == UNUM_UNIT_WIDTH_SHORT || macros.unitWidth == UNUM_UNIT_WIDTH_COUNT) { |
1721 | 0 | return false; // Default or Bogus |
1722 | 0 | } |
1723 | 0 | enum_to_stem_string::unitWidth(macros.unitWidth, sb); |
1724 | 0 | return true; |
1725 | 0 | } |
1726 | | |
1727 | 0 | bool GeneratorHelpers::sign(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { |
1728 | 0 | if (macros.sign == UNUM_SIGN_AUTO || macros.sign == UNUM_SIGN_COUNT) { |
1729 | 0 | return false; // Default or Bogus |
1730 | 0 | } |
1731 | 0 | enum_to_stem_string::signDisplay(macros.sign, sb); |
1732 | 0 | return true; |
1733 | 0 | } |
1734 | | |
1735 | 0 | bool GeneratorHelpers::decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { |
1736 | 0 | if (macros.decimal == UNUM_DECIMAL_SEPARATOR_AUTO || macros.decimal == UNUM_DECIMAL_SEPARATOR_COUNT) { |
1737 | 0 | return false; // Default or Bogus |
1738 | 0 | } |
1739 | 0 | enum_to_stem_string::decimalSeparatorDisplay(macros.decimal, sb); |
1740 | 0 | return true; |
1741 | 0 | } |
1742 | | |
1743 | 0 | bool GeneratorHelpers::scale(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { |
1744 | 0 | if (!macros.scale.isValid()) { |
1745 | 0 | return false; // Default or Bogus |
1746 | 0 | } |
1747 | 0 | sb.append(u"scale/", -1); |
1748 | 0 | blueprint_helpers::generateScaleOption( |
1749 | 0 | macros.scale.fMagnitude, |
1750 | 0 | macros.scale.fArbitrary, |
1751 | 0 | sb, |
1752 | 0 | status); |
1753 | 0 | return true; |
1754 | 0 | } |
1755 | | |
1756 | | |
1757 | | // Definitions of public API methods (put here for dependency disentanglement) |
1758 | | |
1759 | | #if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) |
1760 | | // Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method |
1761 | | // is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation |
1762 | | // inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is |
1763 | | // fully defined. However, since each translation unit explicitly instantiates all the necessary template classes, |
1764 | | // they will all be passed to the linker, and the linker will still find and export all the class members. |
1765 | | #pragma warning(push) |
1766 | | #pragma warning(disable: 4661) |
1767 | | #endif |
1768 | | |
1769 | | template<typename Derived> |
1770 | 0 | UnicodeString NumberFormatterSettings<Derived>::toSkeleton(UErrorCode& status) const { |
1771 | 0 | if (U_FAILURE(status)) { |
1772 | 0 | return ICU_Utility::makeBogusString(); |
1773 | 0 | } |
1774 | 0 | if (fMacros.copyErrorTo(status)) { |
1775 | 0 | return ICU_Utility::makeBogusString(); |
1776 | 0 | } |
1777 | 0 | return skeleton::generate(fMacros, status); |
1778 | 0 | } Unexecuted instantiation: icu_70::number::NumberFormatterSettings<icu_70::number::UnlocalizedNumberFormatter>::toSkeleton(UErrorCode&) const Unexecuted instantiation: icu_70::number::NumberFormatterSettings<icu_70::number::LocalizedNumberFormatter>::toSkeleton(UErrorCode&) const |
1779 | | |
1780 | | // Declare all classes that implement NumberFormatterSettings |
1781 | | // See https://stackoverflow.com/a/495056/1407170 |
1782 | | template |
1783 | | class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>; |
1784 | | template |
1785 | | class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>; |
1786 | | |
1787 | | UnlocalizedNumberFormatter |
1788 | 0 | NumberFormatter::forSkeleton(const UnicodeString& skeleton, UErrorCode& status) { |
1789 | 0 | return skeleton::create(skeleton, nullptr, status); |
1790 | 0 | } |
1791 | | |
1792 | | UnlocalizedNumberFormatter |
1793 | 0 | NumberFormatter::forSkeleton(const UnicodeString& skeleton, UParseError& perror, UErrorCode& status) { |
1794 | 0 | return skeleton::create(skeleton, &perror, status); |
1795 | 0 | } |
1796 | | |
1797 | | #if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) |
1798 | | // Warning 4661. |
1799 | | #pragma warning(pop) |
1800 | | #endif |
1801 | | |
1802 | | #endif /* #if !UCONFIG_NO_FORMATTING */ |