1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
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
12 #include "number_decnum.h"
13 #include "number_skeletons.h"
16 #include "patternprops.h"
17 #include "unicode/ucharstriebuilder.h"
18 #include "number_utils.h"
19 #include "number_decimalquantity.h"
20 #include "unicode/numberformatter.h"
25 using namespace icu::number
;
26 using namespace icu::number::impl
;
27 using namespace icu::number::impl::skeleton
;
31 icu::UInitOnce gNumberSkeletonsInitOnce
= U_INITONCE_INITIALIZER
;
33 char16_t* kSerializedStemTrie
= nullptr;
35 UBool U_CALLCONV
cleanupNumberSkeletons() {
36 uprv_free(kSerializedStemTrie
);
37 kSerializedStemTrie
= nullptr;
38 gNumberSkeletonsInitOnce
.reset();
42 void U_CALLCONV
initNumberSkeletons(UErrorCode
& status
) {
43 ucln_i18n_registerCleanup(UCLN_I18N_NUMBER_SKELETONS
, cleanupNumberSkeletons
);
45 UCharsTrieBuilder
b(status
);
46 if (U_FAILURE(status
)) { return; }
49 b
.add(u
"compact-short", STEM_COMPACT_SHORT
, status
);
50 b
.add(u
"compact-long", STEM_COMPACT_LONG
, status
);
51 b
.add(u
"scientific", STEM_SCIENTIFIC
, status
);
52 b
.add(u
"engineering", STEM_ENGINEERING
, status
);
53 b
.add(u
"notation-simple", STEM_NOTATION_SIMPLE
, status
);
54 b
.add(u
"base-unit", STEM_BASE_UNIT
, status
);
55 b
.add(u
"percent", STEM_PERCENT
, status
);
56 b
.add(u
"permille", STEM_PERMILLE
, status
);
57 b
.add(u
"precision-integer", STEM_PRECISION_INTEGER
, status
);
58 b
.add(u
"precision-unlimited", STEM_PRECISION_UNLIMITED
, status
);
59 b
.add(u
"precision-currency-standard", STEM_PRECISION_CURRENCY_STANDARD
, status
);
60 b
.add(u
"precision-currency-cash", STEM_PRECISION_CURRENCY_CASH
, status
);
61 b
.add(u
"rounding-mode-ceiling", STEM_ROUNDING_MODE_CEILING
, status
);
62 b
.add(u
"rounding-mode-floor", STEM_ROUNDING_MODE_FLOOR
, status
);
63 b
.add(u
"rounding-mode-down", STEM_ROUNDING_MODE_DOWN
, status
);
64 b
.add(u
"rounding-mode-up", STEM_ROUNDING_MODE_UP
, status
);
65 b
.add(u
"rounding-mode-half-even", STEM_ROUNDING_MODE_HALF_EVEN
, status
);
66 b
.add(u
"rounding-mode-half-down", STEM_ROUNDING_MODE_HALF_DOWN
, status
);
67 b
.add(u
"rounding-mode-half-up", STEM_ROUNDING_MODE_HALF_UP
, status
);
68 b
.add(u
"rounding-mode-unnecessary", STEM_ROUNDING_MODE_UNNECESSARY
, status
);
69 b
.add(u
"group-off", STEM_GROUP_OFF
, status
);
70 b
.add(u
"group-min2", STEM_GROUP_MIN2
, status
);
71 b
.add(u
"group-auto", STEM_GROUP_AUTO
, status
);
72 b
.add(u
"group-on-aligned", STEM_GROUP_ON_ALIGNED
, status
);
73 b
.add(u
"group-thousands", STEM_GROUP_THOUSANDS
, status
);
74 b
.add(u
"latin", STEM_LATIN
, status
);
75 b
.add(u
"unit-width-narrow", STEM_UNIT_WIDTH_NARROW
, status
);
76 b
.add(u
"unit-width-short", STEM_UNIT_WIDTH_SHORT
, status
);
77 b
.add(u
"unit-width-full-name", STEM_UNIT_WIDTH_FULL_NAME
, status
);
78 b
.add(u
"unit-width-iso-code", STEM_UNIT_WIDTH_ISO_CODE
, status
);
79 b
.add(u
"unit-width-hidden", STEM_UNIT_WIDTH_HIDDEN
, status
);
80 b
.add(u
"sign-auto", STEM_SIGN_AUTO
, status
);
81 b
.add(u
"sign-always", STEM_SIGN_ALWAYS
, status
);
82 b
.add(u
"sign-never", STEM_SIGN_NEVER
, status
);
83 b
.add(u
"sign-accounting", STEM_SIGN_ACCOUNTING
, status
);
84 b
.add(u
"sign-accounting-always", STEM_SIGN_ACCOUNTING_ALWAYS
, status
);
85 b
.add(u
"sign-except-zero", STEM_SIGN_EXCEPT_ZERO
, status
);
86 b
.add(u
"sign-accounting-except-zero", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO
, status
);
87 b
.add(u
"decimal-auto", STEM_DECIMAL_AUTO
, status
);
88 b
.add(u
"decimal-always", STEM_DECIMAL_ALWAYS
, status
);
89 if (U_FAILURE(status
)) { return; }
92 b
.add(u
"precision-increment", STEM_PRECISION_INCREMENT
, status
);
93 b
.add(u
"measure-unit", STEM_MEASURE_UNIT
, status
);
94 b
.add(u
"per-measure-unit", STEM_PER_MEASURE_UNIT
, status
);
95 b
.add(u
"currency", STEM_CURRENCY
, status
);
96 b
.add(u
"integer-width", STEM_INTEGER_WIDTH
, status
);
97 b
.add(u
"numbering-system", STEM_NUMBERING_SYSTEM
, status
);
98 b
.add(u
"scale", STEM_SCALE
, status
);
99 if (U_FAILURE(status
)) { return; }
101 // Build the CharsTrie
102 // TODO: Use SLOW or FAST here?
103 UnicodeString result
;
104 b
.buildUnicodeString(USTRINGTRIE_BUILD_FAST
, result
, status
);
105 if (U_FAILURE(status
)) { return; }
107 // Copy the result into the global constant pointer
108 size_t numBytes
= result
.length() * sizeof(char16_t);
109 kSerializedStemTrie
= static_cast<char16_t*>(uprv_malloc(numBytes
));
110 uprv_memcpy(kSerializedStemTrie
, result
.getBuffer(), numBytes
);
114 inline void appendMultiple(UnicodeString
& sb
, UChar32 cp
, int32_t count
) {
115 for (int i
= 0; i
< count
; i
++) {
121 #define CHECK_NULL(seen, field, status) (void)(seen); /* for auto-format line wrapping */ \
123 if ((seen).field) { \
124 (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
127 (seen).field = true; \
131 #define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \
133 UErrorCode conversionStatus = U_ZERO_ERROR; \
134 (dest).appendInvariantChars({FALSE, (src).getBuffer() + (start), (end) - (start)}, conversionStatus); \
135 if (conversionStatus == U_INVARIANT_CONVERSION_ERROR) { \
136 /* Don't propagate the invariant conversion error; it is a skeleton syntax error */ \
137 (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
139 } else if (U_FAILURE(conversionStatus)) { \
140 (status) = conversionStatus; \
146 } // anonymous namespace
149 Notation
stem_to_object::notation(skeleton::StemEnum stem
) {
151 case STEM_COMPACT_SHORT
:
152 return Notation::compactShort();
153 case STEM_COMPACT_LONG
:
154 return Notation::compactLong();
155 case STEM_SCIENTIFIC
:
156 return Notation::scientific();
157 case STEM_ENGINEERING
:
158 return Notation::engineering();
159 case STEM_NOTATION_SIMPLE
:
160 return Notation::simple();
163 return Notation::simple(); // return a value: silence compiler warning
167 MeasureUnit
stem_to_object::unit(skeleton::StemEnum stem
) {
171 return NoUnit::base(); // NOLINT
174 return NoUnit::percent(); // NOLINT
177 return NoUnit::permille(); // NOLINT
180 return {}; // return a value: silence compiler warning
184 Precision
stem_to_object::precision(skeleton::StemEnum stem
) {
186 case STEM_PRECISION_INTEGER
:
187 return Precision::integer();
188 case STEM_PRECISION_UNLIMITED
:
189 return Precision::unlimited();
190 case STEM_PRECISION_CURRENCY_STANDARD
:
191 return Precision::currency(UCURR_USAGE_STANDARD
);
192 case STEM_PRECISION_CURRENCY_CASH
:
193 return Precision::currency(UCURR_USAGE_CASH
);
196 return Precision::integer(); // return a value: silence compiler warning
200 UNumberFormatRoundingMode
stem_to_object::roundingMode(skeleton::StemEnum stem
) {
202 case STEM_ROUNDING_MODE_CEILING
:
203 return UNUM_ROUND_CEILING
;
204 case STEM_ROUNDING_MODE_FLOOR
:
205 return UNUM_ROUND_FLOOR
;
206 case STEM_ROUNDING_MODE_DOWN
:
207 return UNUM_ROUND_DOWN
;
208 case STEM_ROUNDING_MODE_UP
:
209 return UNUM_ROUND_UP
;
210 case STEM_ROUNDING_MODE_HALF_EVEN
:
211 return UNUM_ROUND_HALFEVEN
;
212 case STEM_ROUNDING_MODE_HALF_DOWN
:
213 return UNUM_ROUND_HALFDOWN
;
214 case STEM_ROUNDING_MODE_HALF_UP
:
215 return UNUM_ROUND_HALFUP
;
216 case STEM_ROUNDING_MODE_UNNECESSARY
:
217 return UNUM_ROUND_UNNECESSARY
;
220 return UNUM_ROUND_UNNECESSARY
;
224 UGroupingStrategy
stem_to_object::groupingStrategy(skeleton::StemEnum stem
) {
227 return UNUM_GROUPING_OFF
;
228 case STEM_GROUP_MIN2
:
229 return UNUM_GROUPING_MIN2
;
230 case STEM_GROUP_AUTO
:
231 return UNUM_GROUPING_AUTO
;
232 case STEM_GROUP_ON_ALIGNED
:
233 return UNUM_GROUPING_ON_ALIGNED
;
234 case STEM_GROUP_THOUSANDS
:
235 return UNUM_GROUPING_THOUSANDS
;
237 return UNUM_GROUPING_COUNT
; // for objects, throw; for enums, return COUNT
241 UNumberUnitWidth
stem_to_object::unitWidth(skeleton::StemEnum stem
) {
243 case STEM_UNIT_WIDTH_NARROW
:
244 return UNUM_UNIT_WIDTH_NARROW
;
245 case STEM_UNIT_WIDTH_SHORT
:
246 return UNUM_UNIT_WIDTH_SHORT
;
247 case STEM_UNIT_WIDTH_FULL_NAME
:
248 return UNUM_UNIT_WIDTH_FULL_NAME
;
249 case STEM_UNIT_WIDTH_ISO_CODE
:
250 return UNUM_UNIT_WIDTH_ISO_CODE
;
251 case STEM_UNIT_WIDTH_HIDDEN
:
252 return UNUM_UNIT_WIDTH_HIDDEN
;
254 return UNUM_UNIT_WIDTH_COUNT
; // for objects, throw; for enums, return COUNT
258 UNumberSignDisplay
stem_to_object::signDisplay(skeleton::StemEnum stem
) {
261 return UNUM_SIGN_AUTO
;
262 case STEM_SIGN_ALWAYS
:
263 return UNUM_SIGN_ALWAYS
;
264 case STEM_SIGN_NEVER
:
265 return UNUM_SIGN_NEVER
;
266 case STEM_SIGN_ACCOUNTING
:
267 return UNUM_SIGN_ACCOUNTING
;
268 case STEM_SIGN_ACCOUNTING_ALWAYS
:
269 return UNUM_SIGN_ACCOUNTING_ALWAYS
;
270 case STEM_SIGN_EXCEPT_ZERO
:
271 return UNUM_SIGN_EXCEPT_ZERO
;
272 case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO
:
273 return UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
;
275 return UNUM_SIGN_COUNT
; // for objects, throw; for enums, return COUNT
279 UNumberDecimalSeparatorDisplay
stem_to_object::decimalSeparatorDisplay(skeleton::StemEnum stem
) {
281 case STEM_DECIMAL_AUTO
:
282 return UNUM_DECIMAL_SEPARATOR_AUTO
;
283 case STEM_DECIMAL_ALWAYS
:
284 return UNUM_DECIMAL_SEPARATOR_ALWAYS
;
286 return UNUM_DECIMAL_SEPARATOR_COUNT
; // for objects, throw; for enums, return COUNT
291 void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value
, UnicodeString
& sb
) {
293 case UNUM_ROUND_CEILING
:
294 sb
.append(u
"rounding-mode-ceiling", -1);
296 case UNUM_ROUND_FLOOR
:
297 sb
.append(u
"rounding-mode-floor", -1);
299 case UNUM_ROUND_DOWN
:
300 sb
.append(u
"rounding-mode-down", -1);
303 sb
.append(u
"rounding-mode-up", -1);
305 case UNUM_ROUND_HALFEVEN
:
306 sb
.append(u
"rounding-mode-half-even", -1);
308 case UNUM_ROUND_HALFDOWN
:
309 sb
.append(u
"rounding-mode-half-down", -1);
311 case UNUM_ROUND_HALFUP
:
312 sb
.append(u
"rounding-mode-half-up", -1);
314 case UNUM_ROUND_UNNECESSARY
:
315 sb
.append(u
"rounding-mode-unnecessary", -1);
322 void enum_to_stem_string::groupingStrategy(UGroupingStrategy value
, UnicodeString
& sb
) {
324 case UNUM_GROUPING_OFF
:
325 sb
.append(u
"group-off", -1);
327 case UNUM_GROUPING_MIN2
:
328 sb
.append(u
"group-min2", -1);
330 case UNUM_GROUPING_AUTO
:
331 sb
.append(u
"group-auto", -1);
333 case UNUM_GROUPING_ON_ALIGNED
:
334 sb
.append(u
"group-on-aligned", -1);
336 case UNUM_GROUPING_THOUSANDS
:
337 sb
.append(u
"group-thousands", -1);
344 void enum_to_stem_string::unitWidth(UNumberUnitWidth value
, UnicodeString
& sb
) {
346 case UNUM_UNIT_WIDTH_NARROW
:
347 sb
.append(u
"unit-width-narrow", -1);
349 case UNUM_UNIT_WIDTH_SHORT
:
350 sb
.append(u
"unit-width-short", -1);
352 case UNUM_UNIT_WIDTH_FULL_NAME
:
353 sb
.append(u
"unit-width-full-name", -1);
355 case UNUM_UNIT_WIDTH_ISO_CODE
:
356 sb
.append(u
"unit-width-iso-code", -1);
358 case UNUM_UNIT_WIDTH_HIDDEN
:
359 sb
.append(u
"unit-width-hidden", -1);
366 void enum_to_stem_string::signDisplay(UNumberSignDisplay value
, UnicodeString
& sb
) {
369 sb
.append(u
"sign-auto", -1);
371 case UNUM_SIGN_ALWAYS
:
372 sb
.append(u
"sign-always", -1);
374 case UNUM_SIGN_NEVER
:
375 sb
.append(u
"sign-never", -1);
377 case UNUM_SIGN_ACCOUNTING
:
378 sb
.append(u
"sign-accounting", -1);
380 case UNUM_SIGN_ACCOUNTING_ALWAYS
:
381 sb
.append(u
"sign-accounting-always", -1);
383 case UNUM_SIGN_EXCEPT_ZERO
:
384 sb
.append(u
"sign-except-zero", -1);
386 case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
:
387 sb
.append(u
"sign-accounting-except-zero", -1);
395 enum_to_stem_string::decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value
, UnicodeString
& sb
) {
397 case UNUM_DECIMAL_SEPARATOR_AUTO
:
398 sb
.append(u
"decimal-auto", -1);
400 case UNUM_DECIMAL_SEPARATOR_ALWAYS
:
401 sb
.append(u
"decimal-always", -1);
409 UnlocalizedNumberFormatter
skeleton::create(const UnicodeString
& skeletonString
, UErrorCode
& status
) {
410 umtx_initOnce(gNumberSkeletonsInitOnce
, &initNumberSkeletons
, status
);
411 MacroProps macros
= parseSkeleton(skeletonString
, status
);
412 return NumberFormatter::with().macros(macros
);
415 UnicodeString
skeleton::generate(const MacroProps
& macros
, UErrorCode
& status
) {
416 umtx_initOnce(gNumberSkeletonsInitOnce
, &initNumberSkeletons
, status
);
418 GeneratorHelpers::generateSkeleton(macros
, sb
, status
);
422 MacroProps
skeleton::parseSkeleton(const UnicodeString
& skeletonString
, UErrorCode
& status
) {
423 if (U_FAILURE(status
)) { return MacroProps(); }
425 // Add a trailing whitespace to the end of the skeleton string to make code cleaner.
426 UnicodeString
tempSkeletonString(skeletonString
);
427 tempSkeletonString
.append(u
' ');
431 StringSegment
segment(tempSkeletonString
, false);
432 UCharsTrie
stemTrie(kSerializedStemTrie
);
433 ParseState stem
= STATE_NULL
;
436 // Primary skeleton parse loop:
437 while (offset
< segment
.length()) {
438 UChar32 cp
= segment
.codePointAt(offset
);
439 bool isTokenSeparator
= PatternProps::isWhiteSpace(cp
);
440 bool isOptionSeparator
= (cp
== u
'/');
442 if (!isTokenSeparator
&& !isOptionSeparator
) {
443 // Non-separator token; consume it.
444 offset
+= U16_LENGTH(cp
);
445 if (stem
== STATE_NULL
) {
446 // We are currently consuming a stem.
447 // Go to the next state in the stem trie.
448 stemTrie
.nextForCodePoint(cp
);
453 // We are looking at a token or option separator.
454 // If the segment is nonempty, parse it and reset the segment.
455 // Otherwise, make sure it is a valid repeating separator.
457 segment
.setLength(offset
);
458 if (stem
== STATE_NULL
) {
459 // The first separator after the start of a token. Parse it as a stem.
460 stem
= parseStem(segment
, stemTrie
, seen
, macros
, status
);
463 // A separator after the first separator of a token. Parse it as an option.
464 stem
= parseOption(stem
, segment
, macros
, status
);
466 segment
.resetLength();
467 if (U_FAILURE(status
)) { return macros
; }
469 // Consume the segment:
470 segment
.adjustOffset(offset
);
473 } else if (stem
!= STATE_NULL
) {
474 // A separator ('/' or whitespace) following an option separator ('/')
475 // segment.setLength(U16_LENGTH(cp)); // for error message
476 // throw new SkeletonSyntaxException("Unexpected separator character", segment);
477 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
481 // Two spaces in a row; this is OK.
484 // Does the current stem forbid options?
485 if (isOptionSeparator
&& stem
== STATE_NULL
) {
486 // segment.setLength(U16_LENGTH(cp)); // for error message
487 // throw new SkeletonSyntaxException("Unexpected option separator", segment);
488 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
492 // Does the current stem require an option?
493 if (isTokenSeparator
&& stem
!= STATE_NULL
) {
495 case STATE_INCREMENT_PRECISION
:
496 case STATE_MEASURE_UNIT
:
497 case STATE_PER_MEASURE_UNIT
:
498 case STATE_CURRENCY_UNIT
:
499 case STATE_INTEGER_WIDTH
:
500 case STATE_NUMBERING_SYSTEM
:
502 // segment.setLength(U16_LENGTH(cp)); // for error message
503 // throw new SkeletonSyntaxException("Stem requires an option", segment);
504 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
512 // Consume the separator:
513 segment
.adjustOffset(U16_LENGTH(cp
));
515 U_ASSERT(stem
== STATE_NULL
);
520 skeleton::parseStem(const StringSegment
& segment
, const UCharsTrie
& stemTrie
, SeenMacroProps
& seen
,
521 MacroProps
& macros
, UErrorCode
& status
) {
522 // First check for "blueprint" stems, which start with a "signal char"
523 switch (segment
.charAt(0)) {
525 CHECK_NULL(seen
, precision
, status
);
526 blueprint_helpers::parseFractionStem(segment
, macros
, status
);
527 return STATE_FRACTION_PRECISION
;
529 CHECK_NULL(seen
, precision
, status
);
530 blueprint_helpers::parseDigitsStem(segment
, macros
, status
);
536 // Now look at the stemsTrie, which is already be pointing at our stem.
537 UStringTrieResult stemResult
= stemTrie
.current();
539 if (stemResult
!= USTRINGTRIE_INTERMEDIATE_VALUE
&& stemResult
!= USTRINGTRIE_FINAL_VALUE
) {
540 // throw new SkeletonSyntaxException("Unknown stem", segment);
541 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
545 auto stem
= static_cast<StemEnum
>(stemTrie
.getValue());
548 // Stems with meaning on their own, not requiring an option:
550 case STEM_COMPACT_SHORT
:
551 case STEM_COMPACT_LONG
:
552 case STEM_SCIENTIFIC
:
553 case STEM_ENGINEERING
:
554 case STEM_NOTATION_SIMPLE
:
555 CHECK_NULL(seen
, notation
, status
);
556 macros
.notation
= stem_to_object::notation(stem
);
558 case STEM_SCIENTIFIC
:
559 case STEM_ENGINEERING
:
560 return STATE_SCIENTIFIC
; // allows for scientific options
568 CHECK_NULL(seen
, unit
, status
);
569 macros
.unit
= stem_to_object::unit(stem
);
572 case STEM_PRECISION_INTEGER
:
573 case STEM_PRECISION_UNLIMITED
:
574 case STEM_PRECISION_CURRENCY_STANDARD
:
575 case STEM_PRECISION_CURRENCY_CASH
:
576 CHECK_NULL(seen
, precision
, status
);
577 macros
.precision
= stem_to_object::precision(stem
);
579 case STEM_PRECISION_INTEGER
:
580 return STATE_FRACTION_PRECISION
; // allows for "precision-integer/@##"
585 case STEM_ROUNDING_MODE_CEILING
:
586 case STEM_ROUNDING_MODE_FLOOR
:
587 case STEM_ROUNDING_MODE_DOWN
:
588 case STEM_ROUNDING_MODE_UP
:
589 case STEM_ROUNDING_MODE_HALF_EVEN
:
590 case STEM_ROUNDING_MODE_HALF_DOWN
:
591 case STEM_ROUNDING_MODE_HALF_UP
:
592 case STEM_ROUNDING_MODE_UNNECESSARY
:
593 CHECK_NULL(seen
, roundingMode
, status
);
594 macros
.roundingMode
= stem_to_object::roundingMode(stem
);
598 case STEM_GROUP_MIN2
:
599 case STEM_GROUP_AUTO
:
600 case STEM_GROUP_ON_ALIGNED
:
601 case STEM_GROUP_THOUSANDS
:
602 CHECK_NULL(seen
, grouper
, status
);
603 macros
.grouper
= Grouper::forStrategy(stem_to_object::groupingStrategy(stem
));
607 CHECK_NULL(seen
, symbols
, status
);
608 macros
.symbols
.setTo(NumberingSystem::createInstanceByName("latn", status
));
611 case STEM_UNIT_WIDTH_NARROW
:
612 case STEM_UNIT_WIDTH_SHORT
:
613 case STEM_UNIT_WIDTH_FULL_NAME
:
614 case STEM_UNIT_WIDTH_ISO_CODE
:
615 case STEM_UNIT_WIDTH_HIDDEN
:
616 CHECK_NULL(seen
, unitWidth
, status
);
617 macros
.unitWidth
= stem_to_object::unitWidth(stem
);
621 case STEM_SIGN_ALWAYS
:
622 case STEM_SIGN_NEVER
:
623 case STEM_SIGN_ACCOUNTING
:
624 case STEM_SIGN_ACCOUNTING_ALWAYS
:
625 case STEM_SIGN_EXCEPT_ZERO
:
626 case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO
:
627 CHECK_NULL(seen
, sign
, status
);
628 macros
.sign
= stem_to_object::signDisplay(stem
);
631 case STEM_DECIMAL_AUTO
:
632 case STEM_DECIMAL_ALWAYS
:
633 CHECK_NULL(seen
, decimal
, status
);
634 macros
.decimal
= stem_to_object::decimalSeparatorDisplay(stem
);
637 // Stems requiring an option:
639 case STEM_PRECISION_INCREMENT
:
640 CHECK_NULL(seen
, precision
, status
);
641 return STATE_INCREMENT_PRECISION
;
643 case STEM_MEASURE_UNIT
:
644 CHECK_NULL(seen
, unit
, status
);
645 return STATE_MEASURE_UNIT
;
647 case STEM_PER_MEASURE_UNIT
:
648 CHECK_NULL(seen
, perUnit
, status
);
649 return STATE_PER_MEASURE_UNIT
;
652 CHECK_NULL(seen
, unit
, status
);
653 return STATE_CURRENCY_UNIT
;
655 case STEM_INTEGER_WIDTH
:
656 CHECK_NULL(seen
, integerWidth
, status
);
657 return STATE_INTEGER_WIDTH
;
659 case STEM_NUMBERING_SYSTEM
:
660 CHECK_NULL(seen
, symbols
, status
);
661 return STATE_NUMBERING_SYSTEM
;
664 CHECK_NULL(seen
, scale
, status
);
669 return STATE_NULL
; // return a value: silence compiler warning
673 ParseState
skeleton::parseOption(ParseState stem
, const StringSegment
& segment
, MacroProps
& macros
,
674 UErrorCode
& status
) {
676 ///// Required options: /////
679 case STATE_CURRENCY_UNIT
:
680 blueprint_helpers::parseCurrencyOption(segment
, macros
, status
);
682 case STATE_MEASURE_UNIT
:
683 blueprint_helpers::parseMeasureUnitOption(segment
, macros
, status
);
685 case STATE_PER_MEASURE_UNIT
:
686 blueprint_helpers::parseMeasurePerUnitOption(segment
, macros
, status
);
688 case STATE_INCREMENT_PRECISION
:
689 blueprint_helpers::parseIncrementOption(segment
, macros
, status
);
691 case STATE_INTEGER_WIDTH
:
692 blueprint_helpers::parseIntegerWidthOption(segment
, macros
, status
);
694 case STATE_NUMBERING_SYSTEM
:
695 blueprint_helpers::parseNumberingSystemOption(segment
, macros
, status
);
698 blueprint_helpers::parseScaleOption(segment
, macros
, status
);
704 ///// Non-required options: /////
706 // Scientific options
708 case STATE_SCIENTIFIC
:
709 if (blueprint_helpers::parseExponentWidthOption(segment
, macros
, status
)) {
710 return STATE_SCIENTIFIC
;
712 if (U_FAILURE(status
)) {
715 if (blueprint_helpers::parseExponentSignOption(segment
, macros
, status
)) {
716 return STATE_SCIENTIFIC
;
718 if (U_FAILURE(status
)) {
728 case STATE_FRACTION_PRECISION
:
729 if (blueprint_helpers::parseFracSigOption(segment
, macros
, status
)) {
732 if (U_FAILURE(status
)) {
741 // throw new SkeletonSyntaxException("Invalid option", segment);
742 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
746 void GeneratorHelpers::generateSkeleton(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
747 if (U_FAILURE(status
)) { return; }
750 if (GeneratorHelpers::notation(macros
, sb
, status
)) {
753 if (U_FAILURE(status
)) { return; }
754 if (GeneratorHelpers::unit(macros
, sb
, status
)) {
757 if (U_FAILURE(status
)) { return; }
758 if (GeneratorHelpers::perUnit(macros
, sb
, status
)) {
761 if (U_FAILURE(status
)) { return; }
762 if (GeneratorHelpers::precision(macros
, sb
, status
)) {
765 if (U_FAILURE(status
)) { return; }
766 if (GeneratorHelpers::roundingMode(macros
, sb
, status
)) {
769 if (U_FAILURE(status
)) { return; }
770 if (GeneratorHelpers::grouping(macros
, sb
, status
)) {
773 if (U_FAILURE(status
)) { return; }
774 if (GeneratorHelpers::integerWidth(macros
, sb
, status
)) {
777 if (U_FAILURE(status
)) { return; }
778 if (GeneratorHelpers::symbols(macros
, sb
, status
)) {
781 if (U_FAILURE(status
)) { return; }
782 if (GeneratorHelpers::unitWidth(macros
, sb
, status
)) {
785 if (U_FAILURE(status
)) { return; }
786 if (GeneratorHelpers::sign(macros
, sb
, status
)) {
789 if (U_FAILURE(status
)) { return; }
790 if (GeneratorHelpers::decimal(macros
, sb
, status
)) {
793 if (U_FAILURE(status
)) { return; }
794 if (GeneratorHelpers::scale(macros
, sb
, status
)) {
797 if (U_FAILURE(status
)) { return; }
799 // Unsupported options
800 if (!macros
.padder
.isBogus()) {
801 status
= U_UNSUPPORTED_ERROR
;
804 if (macros
.affixProvider
!= nullptr) {
805 status
= U_UNSUPPORTED_ERROR
;
808 if (macros
.rules
!= nullptr) {
809 status
= U_UNSUPPORTED_ERROR
;
812 if (macros
.currencySymbols
!= nullptr) {
813 status
= U_UNSUPPORTED_ERROR
;
817 // Remove the trailing space
818 if (sb
.length() > 0) {
819 sb
.truncate(sb
.length() - 1);
824 bool blueprint_helpers::parseExponentWidthOption(const StringSegment
& segment
, MacroProps
& macros
,
826 if (segment
.charAt(0) != u
'+') {
831 for (; offset
< segment
.length(); offset
++) {
832 if (segment
.charAt(offset
) == u
'e') {
838 if (offset
< segment
.length()) {
841 // Use the public APIs to enforce bounds checking
842 macros
.notation
= static_cast<ScientificNotation
&>(macros
.notation
).withMinExponentDigits(minExp
);
847 blueprint_helpers::generateExponentWidthOption(int32_t minExponentDigits
, UnicodeString
& sb
, UErrorCode
&) {
849 appendMultiple(sb
, u
'e', minExponentDigits
);
853 blueprint_helpers::parseExponentSignOption(const StringSegment
& segment
, MacroProps
& macros
, UErrorCode
&) {
854 // Get the sign display type out of the CharsTrie data structure.
855 UCharsTrie
tempStemTrie(kSerializedStemTrie
);
856 UStringTrieResult result
= tempStemTrie
.next(
857 segment
.toTempUnicodeString().getBuffer(),
859 if (result
!= USTRINGTRIE_INTERMEDIATE_VALUE
&& result
!= USTRINGTRIE_FINAL_VALUE
) {
862 auto sign
= stem_to_object::signDisplay(static_cast<StemEnum
>(tempStemTrie
.getValue()));
863 if (sign
== UNUM_SIGN_COUNT
) {
866 macros
.notation
= static_cast<ScientificNotation
&>(macros
.notation
).withExponentSignDisplay(sign
);
870 void blueprint_helpers::parseCurrencyOption(const StringSegment
& segment
, MacroProps
& macros
,
871 UErrorCode
& status
) {
872 // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us
873 if (segment
.length() != 3) {
874 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
877 const UChar
* currencyCode
= segment
.toTempUnicodeString().getBuffer();
878 UErrorCode localStatus
= U_ZERO_ERROR
;
879 CurrencyUnit
currency(currencyCode
, localStatus
);
880 if (U_FAILURE(localStatus
)) {
882 // throw new SkeletonSyntaxException("Invalid currency", segment);
883 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
887 macros
.unit
= currency
; // NOLINT
891 blueprint_helpers::generateCurrencyOption(const CurrencyUnit
& currency
, UnicodeString
& sb
, UErrorCode
&) {
892 sb
.append(currency
.getISOCurrency(), -1);
895 void blueprint_helpers::parseMeasureUnitOption(const StringSegment
& segment
, MacroProps
& macros
,
896 UErrorCode
& status
) {
897 const UnicodeString stemString
= segment
.toTempUnicodeString();
899 // NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
900 // http://unicode.org/reports/tr35/#Validity_Data
902 while (firstHyphen
< stemString
.length() && stemString
.charAt(firstHyphen
) != '-') {
905 if (firstHyphen
== stemString
.length()) {
906 // throw new SkeletonSyntaxException("Invalid measure unit option", segment);
907 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
911 // Need to do char <-> UChar conversion...
912 U_ASSERT(U_SUCCESS(status
));
914 SKELETON_UCHAR_TO_CHAR(type
, stemString
, 0, firstHyphen
, status
);
916 SKELETON_UCHAR_TO_CHAR(subType
, stemString
, firstHyphen
+ 1, stemString
.length(), status
);
918 // Note: the largest type as of this writing (March 2018) is "volume", which has 24 units.
919 static constexpr int32_t CAPACITY
= 30;
920 MeasureUnit units
[CAPACITY
];
921 UErrorCode localStatus
= U_ZERO_ERROR
;
922 int32_t numUnits
= MeasureUnit::getAvailable(type
.data(), units
, CAPACITY
, localStatus
);
923 if (U_FAILURE(localStatus
)) {
924 // More than 30 units in this type?
925 status
= U_INTERNAL_PROGRAM_ERROR
;
928 for (int32_t i
= 0; i
< numUnits
; i
++) {
929 auto& unit
= units
[i
];
930 if (uprv_strcmp(subType
.data(), unit
.getSubtype()) == 0) {
936 // throw new SkeletonSyntaxException("Unknown measure unit", segment);
937 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
940 void blueprint_helpers::generateMeasureUnitOption(const MeasureUnit
& measureUnit
, UnicodeString
& sb
,
942 // Need to do char <-> UChar conversion...
943 sb
.append(UnicodeString(measureUnit
.getType(), -1, US_INV
));
945 sb
.append(UnicodeString(measureUnit
.getSubtype(), -1, US_INV
));
948 void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment
& segment
, MacroProps
& macros
,
949 UErrorCode
& status
) {
950 // A little bit of a hack: safe the current unit (numerator), call the main measure unit
951 // parsing code, put back the numerator unit, and put the new unit into per-unit.
952 MeasureUnit numerator
= macros
.unit
;
953 parseMeasureUnitOption(segment
, macros
, status
);
954 if (U_FAILURE(status
)) { return; }
955 macros
.perUnit
= macros
.unit
;
956 macros
.unit
= numerator
;
959 void blueprint_helpers::parseFractionStem(const StringSegment
& segment
, MacroProps
& macros
,
960 UErrorCode
& status
) {
961 U_ASSERT(segment
.charAt(0) == u
'.');
965 for (; offset
< segment
.length(); offset
++) {
966 if (segment
.charAt(offset
) == u
'0') {
972 if (offset
< segment
.length()) {
973 if (segment
.charAt(offset
) == u
'+') {
978 for (; offset
< segment
.length(); offset
++) {
979 if (segment
.charAt(offset
) == u
'#') {
989 if (offset
< segment
.length()) {
990 // throw new SkeletonSyntaxException("Invalid fraction stem", segment);
991 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
994 // Use the public APIs to enforce bounds checking
996 macros
.precision
= Precision::minFraction(minFrac
);
998 macros
.precision
= Precision::minMaxFraction(minFrac
, maxFrac
);
1003 blueprint_helpers::generateFractionStem(int32_t minFrac
, int32_t maxFrac
, UnicodeString
& sb
, UErrorCode
&) {
1004 if (minFrac
== 0 && maxFrac
== 0) {
1005 sb
.append(u
"precision-integer", -1);
1009 appendMultiple(sb
, u
'0', minFrac
);
1010 if (maxFrac
== -1) {
1013 appendMultiple(sb
, u
'#', maxFrac
- minFrac
);
1018 blueprint_helpers::parseDigitsStem(const StringSegment
& segment
, MacroProps
& macros
, UErrorCode
& status
) {
1019 U_ASSERT(segment
.charAt(0) == u
'@');
1023 for (; offset
< segment
.length(); offset
++) {
1024 if (segment
.charAt(offset
) == u
'@') {
1030 if (offset
< segment
.length()) {
1031 if (segment
.charAt(offset
) == u
'+') {
1036 for (; offset
< segment
.length(); offset
++) {
1037 if (segment
.charAt(offset
) == u
'#') {
1047 if (offset
< segment
.length()) {
1048 // throw new SkeletonSyntaxException("Invalid significant digits stem", segment);
1049 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1052 // Use the public APIs to enforce bounds checking
1054 macros
.precision
= Precision::minSignificantDigits(minSig
);
1056 macros
.precision
= Precision::minMaxSignificantDigits(minSig
, maxSig
);
1061 blueprint_helpers::generateDigitsStem(int32_t minSig
, int32_t maxSig
, UnicodeString
& sb
, UErrorCode
&) {
1062 appendMultiple(sb
, u
'@', minSig
);
1066 appendMultiple(sb
, u
'#', maxSig
- minSig
);
1070 bool blueprint_helpers::parseFracSigOption(const StringSegment
& segment
, MacroProps
& macros
,
1071 UErrorCode
& status
) {
1072 if (segment
.charAt(0) != u
'@') {
1078 for (; offset
< segment
.length(); offset
++) {
1079 if (segment
.charAt(offset
) == u
'@') {
1085 // For the frac-sig option, there must be minSig or maxSig but not both.
1086 // Valid: @+, @@+, @@@+
1087 // Valid: @#, @##, @###
1088 // Invalid: @, @@, @@@
1089 // Invalid: @@#, @@##, @@@#
1090 if (offset
< segment
.length()) {
1091 if (segment
.charAt(offset
) == u
'+') {
1094 } else if (minSig
> 1) {
1096 // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
1097 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1101 for (; offset
< segment
.length(); offset
++) {
1102 if (segment
.charAt(offset
) == u
'#') {
1111 // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
1112 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1115 if (offset
< segment
.length()) {
1116 // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
1117 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1121 auto& oldPrecision
= static_cast<const FractionPrecision
&>(macros
.precision
);
1123 macros
.precision
= oldPrecision
.withMinDigits(minSig
);
1125 macros
.precision
= oldPrecision
.withMaxDigits(maxSig
);
1130 void blueprint_helpers::parseIncrementOption(const StringSegment
& segment
, MacroProps
& macros
,
1131 UErrorCode
& status
) {
1132 // Need to do char <-> UChar conversion...
1133 U_ASSERT(U_SUCCESS(status
));
1135 SKELETON_UCHAR_TO_CHAR(buffer
, segment
.toTempUnicodeString(), 0, segment
.length(), status
);
1137 // Utilize DecimalQuantity/decNumber to parse this for us.
1139 UErrorCode localStatus
= U_ZERO_ERROR
;
1140 dq
.setToDecNumber({buffer
.data(), buffer
.length()}, localStatus
);
1141 if (U_FAILURE(localStatus
)) {
1142 // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
1143 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1146 double increment
= dq
.toDouble();
1148 // We also need to figure out how many digits. Do a brute force string operation.
1149 int decimalOffset
= 0;
1150 while (decimalOffset
< segment
.length() && segment
.charAt(decimalOffset
) != '.') {
1153 if (decimalOffset
== segment
.length()) {
1154 macros
.precision
= Precision::increment(increment
);
1156 int32_t fractionLength
= segment
.length() - decimalOffset
- 1;
1157 macros
.precision
= Precision::increment(increment
).withMinFraction(fractionLength
);
1161 void blueprint_helpers::generateIncrementOption(double increment
, int32_t trailingZeros
, UnicodeString
& sb
,
1163 // Utilize DecimalQuantity/double_conversion to format this for us.
1165 dq
.setToDouble(increment
);
1166 dq
.roundToInfinity();
1167 sb
.append(dq
.toPlainString());
1169 // We might need to append extra trailing zeros for min fraction...
1170 if (trailingZeros
> 0) {
1171 appendMultiple(sb
, u
'0', trailingZeros
);
1175 void blueprint_helpers::parseIntegerWidthOption(const StringSegment
& segment
, MacroProps
& macros
,
1176 UErrorCode
& status
) {
1180 if (segment
.charAt(0) == u
'+') {
1186 for (; offset
< segment
.length(); offset
++) {
1187 if (segment
.charAt(offset
) == u
'#') {
1193 if (offset
< segment
.length()) {
1194 for (; offset
< segment
.length(); offset
++) {
1195 if (segment
.charAt(offset
) == u
'0') {
1205 if (offset
< segment
.length()) {
1206 // throw new SkeletonSyntaxException("Invalid integer width stem", segment);
1207 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1210 // Use the public APIs to enforce bounds checking
1212 macros
.integerWidth
= IntegerWidth::zeroFillTo(minInt
);
1214 macros
.integerWidth
= IntegerWidth::zeroFillTo(minInt
).truncateAt(maxInt
);
1218 void blueprint_helpers::generateIntegerWidthOption(int32_t minInt
, int32_t maxInt
, UnicodeString
& sb
,
1223 appendMultiple(sb
, u
'#', maxInt
- minInt
);
1225 appendMultiple(sb
, u
'0', minInt
);
1228 void blueprint_helpers::parseNumberingSystemOption(const StringSegment
& segment
, MacroProps
& macros
,
1229 UErrorCode
& status
) {
1230 // Need to do char <-> UChar conversion...
1231 U_ASSERT(U_SUCCESS(status
));
1233 SKELETON_UCHAR_TO_CHAR(buffer
, segment
.toTempUnicodeString(), 0, segment
.length(), status
);
1235 NumberingSystem
* ns
= NumberingSystem::createInstanceByName(buffer
.data(), status
);
1236 if (ns
== nullptr || U_FAILURE(status
)) {
1237 // This is a skeleton syntax error; don't bubble up the low-level NumberingSystem error
1238 // throw new SkeletonSyntaxException("Unknown numbering system", segment);
1239 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1242 macros
.symbols
.setTo(ns
);
1245 void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem
& ns
, UnicodeString
& sb
,
1247 // Need to do char <-> UChar conversion...
1248 sb
.append(UnicodeString(ns
.getName(), -1, US_INV
));
1251 void blueprint_helpers::parseScaleOption(const StringSegment
& segment
, MacroProps
& macros
,
1252 UErrorCode
& status
) {
1253 // Need to do char <-> UChar conversion...
1254 U_ASSERT(U_SUCCESS(status
));
1256 SKELETON_UCHAR_TO_CHAR(buffer
, segment
.toTempUnicodeString(), 0, segment
.length(), status
);
1258 LocalPointer
<DecNum
> decnum(new DecNum(), status
);
1259 if (U_FAILURE(status
)) { return; }
1260 decnum
->setTo({buffer
.data(), buffer
.length()}, status
);
1261 if (U_FAILURE(status
)) {
1262 // This is a skeleton syntax error; don't let the low-level decnum error bubble up
1263 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1267 // NOTE: The constructor will optimize the decnum for us if possible.
1268 macros
.scale
= {0, decnum
.orphan()};
1271 void blueprint_helpers::generateScaleOption(int32_t magnitude
, const DecNum
* arbitrary
, UnicodeString
& sb
,
1272 UErrorCode
& status
) {
1273 // Utilize DecimalQuantity/double_conversion to format this for us.
1275 if (arbitrary
!= nullptr) {
1276 dq
.setToDecNum(*arbitrary
, status
);
1277 if (U_FAILURE(status
)) { return; }
1281 dq
.adjustMagnitude(magnitude
);
1282 dq
.roundToInfinity();
1283 sb
.append(dq
.toPlainString());
1287 bool GeneratorHelpers::notation(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1288 if (macros
.notation
.fType
== Notation::NTN_COMPACT
) {
1289 UNumberCompactStyle style
= macros
.notation
.fUnion
.compactStyle
;
1290 if (style
== UNumberCompactStyle::UNUM_LONG
) {
1291 sb
.append(u
"compact-long", -1);
1293 } else if (style
== UNumberCompactStyle::UNUM_SHORT
) {
1294 sb
.append(u
"compact-short", -1);
1297 // Compact notation generated from custom data (not supported in skeleton)
1298 // The other compact notations are literals
1299 status
= U_UNSUPPORTED_ERROR
;
1302 } else if (macros
.notation
.fType
== Notation::NTN_SCIENTIFIC
) {
1303 const Notation::ScientificSettings
& impl
= macros
.notation
.fUnion
.scientific
;
1304 if (impl
.fEngineeringInterval
== 3) {
1305 sb
.append(u
"engineering", -1);
1307 sb
.append(u
"scientific", -1);
1309 if (impl
.fMinExponentDigits
> 1) {
1311 blueprint_helpers::generateExponentWidthOption(impl
.fMinExponentDigits
, sb
, status
);
1312 if (U_FAILURE(status
)) {
1316 if (impl
.fExponentSignDisplay
!= UNUM_SIGN_AUTO
) {
1318 enum_to_stem_string::signDisplay(impl
.fExponentSignDisplay
, sb
);
1322 // Default value is not shown in normalized form
1327 bool GeneratorHelpers::unit(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1328 if (utils::unitIsCurrency(macros
.unit
)) {
1329 sb
.append(u
"currency/", -1);
1330 CurrencyUnit
currency(macros
.unit
, status
);
1331 if (U_FAILURE(status
)) {
1334 blueprint_helpers::generateCurrencyOption(currency
, sb
, status
);
1336 } else if (utils::unitIsNoUnit(macros
.unit
)) {
1337 if (utils::unitIsPercent(macros
.unit
)) {
1338 sb
.append(u
"percent", -1);
1340 } else if (utils::unitIsPermille(macros
.unit
)) {
1341 sb
.append(u
"permille", -1);
1344 // Default value is not shown in normalized form
1348 sb
.append(u
"measure-unit/", -1);
1349 blueprint_helpers::generateMeasureUnitOption(macros
.unit
, sb
, status
);
1354 bool GeneratorHelpers::perUnit(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1355 // Per-units are currently expected to be only MeasureUnits.
1356 if (utils::unitIsNoUnit(macros
.perUnit
)) {
1357 if (utils::unitIsPercent(macros
.perUnit
) || utils::unitIsPermille(macros
.perUnit
)) {
1358 status
= U_UNSUPPORTED_ERROR
;
1361 // Default value: ok to ignore
1364 } else if (utils::unitIsCurrency(macros
.perUnit
)) {
1365 status
= U_UNSUPPORTED_ERROR
;
1368 sb
.append(u
"per-measure-unit/", -1);
1369 blueprint_helpers::generateMeasureUnitOption(macros
.perUnit
, sb
, status
);
1374 bool GeneratorHelpers::precision(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1375 if (macros
.precision
.fType
== Precision::RND_NONE
) {
1376 sb
.append(u
"precision-unlimited", -1);
1377 } else if (macros
.precision
.fType
== Precision::RND_FRACTION
) {
1378 const Precision::FractionSignificantSettings
& impl
= macros
.precision
.fUnion
.fracSig
;
1379 blueprint_helpers::generateFractionStem(impl
.fMinFrac
, impl
.fMaxFrac
, sb
, status
);
1380 } else if (macros
.precision
.fType
== Precision::RND_SIGNIFICANT
) {
1381 const Precision::FractionSignificantSettings
& impl
= macros
.precision
.fUnion
.fracSig
;
1382 blueprint_helpers::generateDigitsStem(impl
.fMinSig
, impl
.fMaxSig
, sb
, status
);
1383 } else if (macros
.precision
.fType
== Precision::RND_FRACTION_SIGNIFICANT
) {
1384 const Precision::FractionSignificantSettings
& impl
= macros
.precision
.fUnion
.fracSig
;
1385 blueprint_helpers::generateFractionStem(impl
.fMinFrac
, impl
.fMaxFrac
, sb
, status
);
1387 if (impl
.fMinSig
== -1) {
1388 blueprint_helpers::generateDigitsStem(1, impl
.fMaxSig
, sb
, status
);
1390 blueprint_helpers::generateDigitsStem(impl
.fMinSig
, -1, sb
, status
);
1392 } else if (macros
.precision
.fType
== Precision::RND_INCREMENT
) {
1393 const Precision::IncrementSettings
& impl
= macros
.precision
.fUnion
.increment
;
1394 sb
.append(u
"precision-increment/", -1);
1395 blueprint_helpers::generateIncrementOption(
1397 impl
.fMinFrac
- impl
.fMaxFrac
,
1400 } else if (macros
.precision
.fType
== Precision::RND_CURRENCY
) {
1401 UCurrencyUsage usage
= macros
.precision
.fUnion
.currencyUsage
;
1402 if (usage
== UCURR_USAGE_STANDARD
) {
1403 sb
.append(u
"precision-currency-standard", -1);
1405 sb
.append(u
"precision-currency-cash", -1);
1412 // NOTE: Always return true for rounding because the default value depends on other options.
1416 bool GeneratorHelpers::roundingMode(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1417 if (macros
.roundingMode
== kDefaultMode
) {
1418 return false; // Default
1420 enum_to_stem_string::roundingMode(macros
.roundingMode
, sb
);
1424 bool GeneratorHelpers::grouping(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1425 if (macros
.grouper
.isBogus()) {
1426 return false; // No value
1427 } else if (macros
.grouper
.fStrategy
== UNUM_GROUPING_COUNT
) {
1428 status
= U_UNSUPPORTED_ERROR
;
1430 } else if (macros
.grouper
.fStrategy
== UNUM_GROUPING_AUTO
) {
1431 return false; // Default value
1433 enum_to_stem_string::groupingStrategy(macros
.grouper
.fStrategy
, sb
);
1438 bool GeneratorHelpers::integerWidth(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1439 if (macros
.integerWidth
.fHasError
|| macros
.integerWidth
.isBogus() ||
1440 macros
.integerWidth
== IntegerWidth::standard()) {
1444 sb
.append(u
"integer-width/", -1);
1445 blueprint_helpers::generateIntegerWidthOption(
1446 macros
.integerWidth
.fUnion
.minMaxInt
.fMinInt
,
1447 macros
.integerWidth
.fUnion
.minMaxInt
.fMaxInt
,
1453 bool GeneratorHelpers::symbols(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1454 if (macros
.symbols
.isNumberingSystem()) {
1455 const NumberingSystem
& ns
= *macros
.symbols
.getNumberingSystem();
1456 if (uprv_strcmp(ns
.getName(), "latn") == 0) {
1457 sb
.append(u
"latin", -1);
1459 sb
.append(u
"numbering-system/", -1);
1460 blueprint_helpers::generateNumberingSystemOption(ns
, sb
, status
);
1463 } else if (macros
.symbols
.isDecimalFormatSymbols()) {
1464 status
= U_UNSUPPORTED_ERROR
;
1467 // No custom symbols
1472 bool GeneratorHelpers::unitWidth(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1473 if (macros
.unitWidth
== UNUM_UNIT_WIDTH_SHORT
|| macros
.unitWidth
== UNUM_UNIT_WIDTH_COUNT
) {
1474 return false; // Default or Bogus
1476 enum_to_stem_string::unitWidth(macros
.unitWidth
, sb
);
1480 bool GeneratorHelpers::sign(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1481 if (macros
.sign
== UNUM_SIGN_AUTO
|| macros
.sign
== UNUM_SIGN_COUNT
) {
1482 return false; // Default or Bogus
1484 enum_to_stem_string::signDisplay(macros
.sign
, sb
);
1488 bool GeneratorHelpers::decimal(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1489 if (macros
.decimal
== UNUM_DECIMAL_SEPARATOR_AUTO
|| macros
.decimal
== UNUM_DECIMAL_SEPARATOR_COUNT
) {
1490 return false; // Default or Bogus
1492 enum_to_stem_string::decimalSeparatorDisplay(macros
.decimal
, sb
);
1496 bool GeneratorHelpers::scale(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1497 if (!macros
.scale
.isValid()) {
1498 return false; // Default or Bogus
1500 sb
.append(u
"scale/", -1);
1501 blueprint_helpers::generateScaleOption(
1502 macros
.scale
.fMagnitude
,
1503 macros
.scale
.fArbitrary
,
1510 #endif /* #if !UCONFIG_NO_FORMATTING */