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();
166 MeasureUnit
stem_to_object::unit(skeleton::StemEnum stem
) {
170 return NoUnit::base(); // NOLINT
173 return NoUnit::percent(); // NOLINT
176 return NoUnit::permille(); // NOLINT
182 Precision
stem_to_object::precision(skeleton::StemEnum stem
) {
184 case STEM_PRECISION_INTEGER
:
185 return Precision::integer();
186 case STEM_PRECISION_UNLIMITED
:
187 return Precision::unlimited();
188 case STEM_PRECISION_CURRENCY_STANDARD
:
189 return Precision::currency(UCURR_USAGE_STANDARD
);
190 case STEM_PRECISION_CURRENCY_CASH
:
191 return Precision::currency(UCURR_USAGE_CASH
);
197 UNumberFormatRoundingMode
stem_to_object::roundingMode(skeleton::StemEnum stem
) {
199 case STEM_ROUNDING_MODE_CEILING
:
200 return UNUM_ROUND_CEILING
;
201 case STEM_ROUNDING_MODE_FLOOR
:
202 return UNUM_ROUND_FLOOR
;
203 case STEM_ROUNDING_MODE_DOWN
:
204 return UNUM_ROUND_DOWN
;
205 case STEM_ROUNDING_MODE_UP
:
206 return UNUM_ROUND_UP
;
207 case STEM_ROUNDING_MODE_HALF_EVEN
:
208 return UNUM_ROUND_HALFEVEN
;
209 case STEM_ROUNDING_MODE_HALF_DOWN
:
210 return UNUM_ROUND_HALFDOWN
;
211 case STEM_ROUNDING_MODE_HALF_UP
:
212 return UNUM_ROUND_HALFUP
;
213 case STEM_ROUNDING_MODE_UNNECESSARY
:
214 return UNUM_ROUND_UNNECESSARY
;
220 UNumberGroupingStrategy
stem_to_object::groupingStrategy(skeleton::StemEnum stem
) {
223 return UNUM_GROUPING_OFF
;
224 case STEM_GROUP_MIN2
:
225 return UNUM_GROUPING_MIN2
;
226 case STEM_GROUP_AUTO
:
227 return UNUM_GROUPING_AUTO
;
228 case STEM_GROUP_ON_ALIGNED
:
229 return UNUM_GROUPING_ON_ALIGNED
;
230 case STEM_GROUP_THOUSANDS
:
231 return UNUM_GROUPING_THOUSANDS
;
233 return UNUM_GROUPING_COUNT
; // for objects, throw; for enums, return COUNT
237 UNumberUnitWidth
stem_to_object::unitWidth(skeleton::StemEnum stem
) {
239 case STEM_UNIT_WIDTH_NARROW
:
240 return UNUM_UNIT_WIDTH_NARROW
;
241 case STEM_UNIT_WIDTH_SHORT
:
242 return UNUM_UNIT_WIDTH_SHORT
;
243 case STEM_UNIT_WIDTH_FULL_NAME
:
244 return UNUM_UNIT_WIDTH_FULL_NAME
;
245 case STEM_UNIT_WIDTH_ISO_CODE
:
246 return UNUM_UNIT_WIDTH_ISO_CODE
;
247 case STEM_UNIT_WIDTH_HIDDEN
:
248 return UNUM_UNIT_WIDTH_HIDDEN
;
250 return UNUM_UNIT_WIDTH_COUNT
; // for objects, throw; for enums, return COUNT
254 UNumberSignDisplay
stem_to_object::signDisplay(skeleton::StemEnum stem
) {
257 return UNUM_SIGN_AUTO
;
258 case STEM_SIGN_ALWAYS
:
259 return UNUM_SIGN_ALWAYS
;
260 case STEM_SIGN_NEVER
:
261 return UNUM_SIGN_NEVER
;
262 case STEM_SIGN_ACCOUNTING
:
263 return UNUM_SIGN_ACCOUNTING
;
264 case STEM_SIGN_ACCOUNTING_ALWAYS
:
265 return UNUM_SIGN_ACCOUNTING_ALWAYS
;
266 case STEM_SIGN_EXCEPT_ZERO
:
267 return UNUM_SIGN_EXCEPT_ZERO
;
268 case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO
:
269 return UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
;
271 return UNUM_SIGN_COUNT
; // for objects, throw; for enums, return COUNT
275 UNumberDecimalSeparatorDisplay
stem_to_object::decimalSeparatorDisplay(skeleton::StemEnum stem
) {
277 case STEM_DECIMAL_AUTO
:
278 return UNUM_DECIMAL_SEPARATOR_AUTO
;
279 case STEM_DECIMAL_ALWAYS
:
280 return UNUM_DECIMAL_SEPARATOR_ALWAYS
;
282 return UNUM_DECIMAL_SEPARATOR_COUNT
; // for objects, throw; for enums, return COUNT
287 void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value
, UnicodeString
& sb
) {
289 case UNUM_ROUND_CEILING
:
290 sb
.append(u
"rounding-mode-ceiling", -1);
292 case UNUM_ROUND_FLOOR
:
293 sb
.append(u
"rounding-mode-floor", -1);
295 case UNUM_ROUND_DOWN
:
296 sb
.append(u
"rounding-mode-down", -1);
299 sb
.append(u
"rounding-mode-up", -1);
301 case UNUM_ROUND_HALFEVEN
:
302 sb
.append(u
"rounding-mode-half-even", -1);
304 case UNUM_ROUND_HALFDOWN
:
305 sb
.append(u
"rounding-mode-half-down", -1);
307 case UNUM_ROUND_HALFUP
:
308 sb
.append(u
"rounding-mode-half-up", -1);
310 case UNUM_ROUND_UNNECESSARY
:
311 sb
.append(u
"rounding-mode-unnecessary", -1);
318 void enum_to_stem_string::groupingStrategy(UNumberGroupingStrategy value
, UnicodeString
& sb
) {
320 case UNUM_GROUPING_OFF
:
321 sb
.append(u
"group-off", -1);
323 case UNUM_GROUPING_MIN2
:
324 sb
.append(u
"group-min2", -1);
326 case UNUM_GROUPING_AUTO
:
327 sb
.append(u
"group-auto", -1);
329 case UNUM_GROUPING_ON_ALIGNED
:
330 sb
.append(u
"group-on-aligned", -1);
332 case UNUM_GROUPING_THOUSANDS
:
333 sb
.append(u
"group-thousands", -1);
340 void enum_to_stem_string::unitWidth(UNumberUnitWidth value
, UnicodeString
& sb
) {
342 case UNUM_UNIT_WIDTH_NARROW
:
343 sb
.append(u
"unit-width-narrow", -1);
345 case UNUM_UNIT_WIDTH_SHORT
:
346 sb
.append(u
"unit-width-short", -1);
348 case UNUM_UNIT_WIDTH_FULL_NAME
:
349 sb
.append(u
"unit-width-full-name", -1);
351 case UNUM_UNIT_WIDTH_ISO_CODE
:
352 sb
.append(u
"unit-width-iso-code", -1);
354 case UNUM_UNIT_WIDTH_HIDDEN
:
355 sb
.append(u
"unit-width-hidden", -1);
362 void enum_to_stem_string::signDisplay(UNumberSignDisplay value
, UnicodeString
& sb
) {
365 sb
.append(u
"sign-auto", -1);
367 case UNUM_SIGN_ALWAYS
:
368 sb
.append(u
"sign-always", -1);
370 case UNUM_SIGN_NEVER
:
371 sb
.append(u
"sign-never", -1);
373 case UNUM_SIGN_ACCOUNTING
:
374 sb
.append(u
"sign-accounting", -1);
376 case UNUM_SIGN_ACCOUNTING_ALWAYS
:
377 sb
.append(u
"sign-accounting-always", -1);
379 case UNUM_SIGN_EXCEPT_ZERO
:
380 sb
.append(u
"sign-except-zero", -1);
382 case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
:
383 sb
.append(u
"sign-accounting-except-zero", -1);
391 enum_to_stem_string::decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value
, UnicodeString
& sb
) {
393 case UNUM_DECIMAL_SEPARATOR_AUTO
:
394 sb
.append(u
"decimal-auto", -1);
396 case UNUM_DECIMAL_SEPARATOR_ALWAYS
:
397 sb
.append(u
"decimal-always", -1);
405 UnlocalizedNumberFormatter
skeleton::create(
406 const UnicodeString
& skeletonString
, UParseError
* perror
, UErrorCode
& status
) {
409 if (perror
!= nullptr) {
412 perror
->preContext
[0] = 0;
413 perror
->postContext
[0] = 0;
416 umtx_initOnce(gNumberSkeletonsInitOnce
, &initNumberSkeletons
, status
);
417 if (U_FAILURE(status
)) {
422 MacroProps macros
= parseSkeleton(skeletonString
, errOffset
, status
);
423 if (U_SUCCESS(status
)) {
424 return NumberFormatter::with().macros(macros
);
427 if (perror
== nullptr) {
431 // Populate the UParseError with the error location
432 perror
->offset
= errOffset
;
433 int32_t contextStart
= uprv_max(0, errOffset
- U_PARSE_CONTEXT_LEN
+ 1);
434 int32_t contextEnd
= uprv_min(skeletonString
.length(), errOffset
+ U_PARSE_CONTEXT_LEN
- 1);
435 skeletonString
.extract(contextStart
, errOffset
- contextStart
, perror
->preContext
, 0);
436 perror
->preContext
[errOffset
- contextStart
] = 0;
437 skeletonString
.extract(errOffset
, contextEnd
- errOffset
, perror
->postContext
, 0);
438 perror
->postContext
[contextEnd
- errOffset
] = 0;
442 UnicodeString
skeleton::generate(const MacroProps
& macros
, UErrorCode
& status
) {
443 umtx_initOnce(gNumberSkeletonsInitOnce
, &initNumberSkeletons
, status
);
445 GeneratorHelpers::generateSkeleton(macros
, sb
, status
);
449 MacroProps
skeleton::parseSkeleton(
450 const UnicodeString
& skeletonString
, int32_t& errOffset
, UErrorCode
& status
) {
451 U_ASSERT(U_SUCCESS(status
));
453 // Add a trailing whitespace to the end of the skeleton string to make code cleaner.
454 UnicodeString
tempSkeletonString(skeletonString
);
455 tempSkeletonString
.append(u
' ');
459 StringSegment
segment(tempSkeletonString
, false);
460 UCharsTrie
stemTrie(kSerializedStemTrie
);
461 ParseState stem
= STATE_NULL
;
464 // Primary skeleton parse loop:
465 while (offset
< segment
.length()) {
466 UChar32 cp
= segment
.codePointAt(offset
);
467 bool isTokenSeparator
= PatternProps::isWhiteSpace(cp
);
468 bool isOptionSeparator
= (cp
== u
'/');
470 if (!isTokenSeparator
&& !isOptionSeparator
) {
471 // Non-separator token; consume it.
472 offset
+= U16_LENGTH(cp
);
473 if (stem
== STATE_NULL
) {
474 // We are currently consuming a stem.
475 // Go to the next state in the stem trie.
476 stemTrie
.nextForCodePoint(cp
);
481 // We are looking at a token or option separator.
482 // If the segment is nonempty, parse it and reset the segment.
483 // Otherwise, make sure it is a valid repeating separator.
485 segment
.setLength(offset
);
486 if (stem
== STATE_NULL
) {
487 // The first separator after the start of a token. Parse it as a stem.
488 stem
= parseStem(segment
, stemTrie
, seen
, macros
, status
);
491 // A separator after the first separator of a token. Parse it as an option.
492 stem
= parseOption(stem
, segment
, macros
, status
);
494 segment
.resetLength();
495 if (U_FAILURE(status
)) {
496 errOffset
= segment
.getOffset();
500 // Consume the segment:
501 segment
.adjustOffset(offset
);
504 } else if (stem
!= STATE_NULL
) {
505 // A separator ('/' or whitespace) following an option separator ('/')
506 // segment.setLength(U16_LENGTH(cp)); // for error message
507 // throw new SkeletonSyntaxException("Unexpected separator character", segment);
508 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
509 errOffset
= segment
.getOffset();
513 // Two spaces in a row; this is OK.
516 // Does the current stem forbid options?
517 if (isOptionSeparator
&& stem
== STATE_NULL
) {
518 // segment.setLength(U16_LENGTH(cp)); // for error message
519 // throw new SkeletonSyntaxException("Unexpected option separator", segment);
520 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
521 errOffset
= segment
.getOffset();
525 // Does the current stem require an option?
526 if (isTokenSeparator
&& stem
!= STATE_NULL
) {
528 case STATE_INCREMENT_PRECISION
:
529 case STATE_MEASURE_UNIT
:
530 case STATE_PER_MEASURE_UNIT
:
531 case STATE_CURRENCY_UNIT
:
532 case STATE_INTEGER_WIDTH
:
533 case STATE_NUMBERING_SYSTEM
:
535 // segment.setLength(U16_LENGTH(cp)); // for error message
536 // throw new SkeletonSyntaxException("Stem requires an option", segment);
537 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
538 errOffset
= segment
.getOffset();
546 // Consume the separator:
547 segment
.adjustOffset(U16_LENGTH(cp
));
549 U_ASSERT(stem
== STATE_NULL
);
554 skeleton::parseStem(const StringSegment
& segment
, const UCharsTrie
& stemTrie
, SeenMacroProps
& seen
,
555 MacroProps
& macros
, UErrorCode
& status
) {
556 // First check for "blueprint" stems, which start with a "signal char"
557 switch (segment
.charAt(0)) {
559 CHECK_NULL(seen
, precision
, status
);
560 blueprint_helpers::parseFractionStem(segment
, macros
, status
);
561 return STATE_FRACTION_PRECISION
;
563 CHECK_NULL(seen
, precision
, status
);
564 blueprint_helpers::parseDigitsStem(segment
, macros
, status
);
570 // Now look at the stemsTrie, which is already be pointing at our stem.
571 UStringTrieResult stemResult
= stemTrie
.current();
573 if (stemResult
!= USTRINGTRIE_INTERMEDIATE_VALUE
&& stemResult
!= USTRINGTRIE_FINAL_VALUE
) {
574 // throw new SkeletonSyntaxException("Unknown stem", segment);
575 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
579 auto stem
= static_cast<StemEnum
>(stemTrie
.getValue());
582 // Stems with meaning on their own, not requiring an option:
584 case STEM_COMPACT_SHORT
:
585 case STEM_COMPACT_LONG
:
586 case STEM_SCIENTIFIC
:
587 case STEM_ENGINEERING
:
588 case STEM_NOTATION_SIMPLE
:
589 CHECK_NULL(seen
, notation
, status
);
590 macros
.notation
= stem_to_object::notation(stem
);
592 case STEM_SCIENTIFIC
:
593 case STEM_ENGINEERING
:
594 return STATE_SCIENTIFIC
; // allows for scientific options
602 CHECK_NULL(seen
, unit
, status
);
603 macros
.unit
= stem_to_object::unit(stem
);
606 case STEM_PRECISION_INTEGER
:
607 case STEM_PRECISION_UNLIMITED
:
608 case STEM_PRECISION_CURRENCY_STANDARD
:
609 case STEM_PRECISION_CURRENCY_CASH
:
610 CHECK_NULL(seen
, precision
, status
);
611 macros
.precision
= stem_to_object::precision(stem
);
613 case STEM_PRECISION_INTEGER
:
614 return STATE_FRACTION_PRECISION
; // allows for "precision-integer/@##"
619 case STEM_ROUNDING_MODE_CEILING
:
620 case STEM_ROUNDING_MODE_FLOOR
:
621 case STEM_ROUNDING_MODE_DOWN
:
622 case STEM_ROUNDING_MODE_UP
:
623 case STEM_ROUNDING_MODE_HALF_EVEN
:
624 case STEM_ROUNDING_MODE_HALF_DOWN
:
625 case STEM_ROUNDING_MODE_HALF_UP
:
626 case STEM_ROUNDING_MODE_UNNECESSARY
:
627 CHECK_NULL(seen
, roundingMode
, status
);
628 macros
.roundingMode
= stem_to_object::roundingMode(stem
);
632 case STEM_GROUP_MIN2
:
633 case STEM_GROUP_AUTO
:
634 case STEM_GROUP_ON_ALIGNED
:
635 case STEM_GROUP_THOUSANDS
:
636 CHECK_NULL(seen
, grouper
, status
);
637 macros
.grouper
= Grouper::forStrategy(stem_to_object::groupingStrategy(stem
));
641 CHECK_NULL(seen
, symbols
, status
);
642 macros
.symbols
.setTo(NumberingSystem::createInstanceByName("latn", status
));
645 case STEM_UNIT_WIDTH_NARROW
:
646 case STEM_UNIT_WIDTH_SHORT
:
647 case STEM_UNIT_WIDTH_FULL_NAME
:
648 case STEM_UNIT_WIDTH_ISO_CODE
:
649 case STEM_UNIT_WIDTH_HIDDEN
:
650 CHECK_NULL(seen
, unitWidth
, status
);
651 macros
.unitWidth
= stem_to_object::unitWidth(stem
);
655 case STEM_SIGN_ALWAYS
:
656 case STEM_SIGN_NEVER
:
657 case STEM_SIGN_ACCOUNTING
:
658 case STEM_SIGN_ACCOUNTING_ALWAYS
:
659 case STEM_SIGN_EXCEPT_ZERO
:
660 case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO
:
661 CHECK_NULL(seen
, sign
, status
);
662 macros
.sign
= stem_to_object::signDisplay(stem
);
665 case STEM_DECIMAL_AUTO
:
666 case STEM_DECIMAL_ALWAYS
:
667 CHECK_NULL(seen
, decimal
, status
);
668 macros
.decimal
= stem_to_object::decimalSeparatorDisplay(stem
);
671 // Stems requiring an option:
673 case STEM_PRECISION_INCREMENT
:
674 CHECK_NULL(seen
, precision
, status
);
675 return STATE_INCREMENT_PRECISION
;
677 case STEM_MEASURE_UNIT
:
678 CHECK_NULL(seen
, unit
, status
);
679 return STATE_MEASURE_UNIT
;
681 case STEM_PER_MEASURE_UNIT
:
682 CHECK_NULL(seen
, perUnit
, status
);
683 return STATE_PER_MEASURE_UNIT
;
686 CHECK_NULL(seen
, unit
, status
);
687 return STATE_CURRENCY_UNIT
;
689 case STEM_INTEGER_WIDTH
:
690 CHECK_NULL(seen
, integerWidth
, status
);
691 return STATE_INTEGER_WIDTH
;
693 case STEM_NUMBERING_SYSTEM
:
694 CHECK_NULL(seen
, symbols
, status
);
695 return STATE_NUMBERING_SYSTEM
;
698 CHECK_NULL(seen
, scale
, status
);
706 ParseState
skeleton::parseOption(ParseState stem
, const StringSegment
& segment
, MacroProps
& macros
,
707 UErrorCode
& status
) {
709 ///// Required options: /////
712 case STATE_CURRENCY_UNIT
:
713 blueprint_helpers::parseCurrencyOption(segment
, macros
, status
);
715 case STATE_MEASURE_UNIT
:
716 blueprint_helpers::parseMeasureUnitOption(segment
, macros
, status
);
718 case STATE_PER_MEASURE_UNIT
:
719 blueprint_helpers::parseMeasurePerUnitOption(segment
, macros
, status
);
721 case STATE_INCREMENT_PRECISION
:
722 blueprint_helpers::parseIncrementOption(segment
, macros
, status
);
724 case STATE_INTEGER_WIDTH
:
725 blueprint_helpers::parseIntegerWidthOption(segment
, macros
, status
);
727 case STATE_NUMBERING_SYSTEM
:
728 blueprint_helpers::parseNumberingSystemOption(segment
, macros
, status
);
731 blueprint_helpers::parseScaleOption(segment
, macros
, status
);
737 ///// Non-required options: /////
739 // Scientific options
741 case STATE_SCIENTIFIC
:
742 if (blueprint_helpers::parseExponentWidthOption(segment
, macros
, status
)) {
743 return STATE_SCIENTIFIC
;
745 if (U_FAILURE(status
)) {
748 if (blueprint_helpers::parseExponentSignOption(segment
, macros
, status
)) {
749 return STATE_SCIENTIFIC
;
751 if (U_FAILURE(status
)) {
761 case STATE_FRACTION_PRECISION
:
762 if (blueprint_helpers::parseFracSigOption(segment
, macros
, status
)) {
765 if (U_FAILURE(status
)) {
774 // throw new SkeletonSyntaxException("Invalid option", segment);
775 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
779 void GeneratorHelpers::generateSkeleton(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
780 if (U_FAILURE(status
)) { return; }
783 if (GeneratorHelpers::notation(macros
, sb
, status
)) {
786 if (U_FAILURE(status
)) { return; }
787 if (GeneratorHelpers::unit(macros
, sb
, status
)) {
790 if (U_FAILURE(status
)) { return; }
791 if (GeneratorHelpers::perUnit(macros
, sb
, status
)) {
794 if (U_FAILURE(status
)) { return; }
795 if (GeneratorHelpers::precision(macros
, sb
, status
)) {
798 if (U_FAILURE(status
)) { return; }
799 if (GeneratorHelpers::roundingMode(macros
, sb
, status
)) {
802 if (U_FAILURE(status
)) { return; }
803 if (GeneratorHelpers::grouping(macros
, sb
, status
)) {
806 if (U_FAILURE(status
)) { return; }
807 if (GeneratorHelpers::integerWidth(macros
, sb
, status
)) {
810 if (U_FAILURE(status
)) { return; }
811 if (GeneratorHelpers::symbols(macros
, sb
, status
)) {
814 if (U_FAILURE(status
)) { return; }
815 if (GeneratorHelpers::unitWidth(macros
, sb
, status
)) {
818 if (U_FAILURE(status
)) { return; }
819 if (GeneratorHelpers::sign(macros
, sb
, status
)) {
822 if (U_FAILURE(status
)) { return; }
823 if (GeneratorHelpers::decimal(macros
, sb
, status
)) {
826 if (U_FAILURE(status
)) { return; }
827 if (GeneratorHelpers::scale(macros
, sb
, status
)) {
830 if (U_FAILURE(status
)) { return; }
832 // Unsupported options
833 if (!macros
.padder
.isBogus()) {
834 status
= U_UNSUPPORTED_ERROR
;
837 if (macros
.affixProvider
!= nullptr) {
838 status
= U_UNSUPPORTED_ERROR
;
841 if (macros
.rules
!= nullptr) {
842 status
= U_UNSUPPORTED_ERROR
;
845 if (macros
.currencySymbols
!= nullptr) {
846 status
= U_UNSUPPORTED_ERROR
;
850 // Remove the trailing space
851 if (sb
.length() > 0) {
852 sb
.truncate(sb
.length() - 1);
857 bool blueprint_helpers::parseExponentWidthOption(const StringSegment
& segment
, MacroProps
& macros
,
859 if (segment
.charAt(0) != u
'+') {
864 for (; offset
< segment
.length(); offset
++) {
865 if (segment
.charAt(offset
) == u
'e') {
871 if (offset
< segment
.length()) {
874 // Use the public APIs to enforce bounds checking
875 macros
.notation
= static_cast<ScientificNotation
&>(macros
.notation
).withMinExponentDigits(minExp
);
880 blueprint_helpers::generateExponentWidthOption(int32_t minExponentDigits
, UnicodeString
& sb
, UErrorCode
&) {
882 appendMultiple(sb
, u
'e', minExponentDigits
);
886 blueprint_helpers::parseExponentSignOption(const StringSegment
& segment
, MacroProps
& macros
, UErrorCode
&) {
887 // Get the sign display type out of the CharsTrie data structure.
888 UCharsTrie
tempStemTrie(kSerializedStemTrie
);
889 UStringTrieResult result
= tempStemTrie
.next(
890 segment
.toTempUnicodeString().getBuffer(),
892 if (result
!= USTRINGTRIE_INTERMEDIATE_VALUE
&& result
!= USTRINGTRIE_FINAL_VALUE
) {
895 auto sign
= stem_to_object::signDisplay(static_cast<StemEnum
>(tempStemTrie
.getValue()));
896 if (sign
== UNUM_SIGN_COUNT
) {
899 macros
.notation
= static_cast<ScientificNotation
&>(macros
.notation
).withExponentSignDisplay(sign
);
903 void blueprint_helpers::parseCurrencyOption(const StringSegment
& segment
, MacroProps
& macros
,
904 UErrorCode
& status
) {
905 // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us
906 if (segment
.length() != 3) {
907 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
910 const UChar
* currencyCode
= segment
.toTempUnicodeString().getBuffer();
911 UErrorCode localStatus
= U_ZERO_ERROR
;
912 CurrencyUnit
currency(currencyCode
, localStatus
);
913 if (U_FAILURE(localStatus
)) {
915 // throw new SkeletonSyntaxException("Invalid currency", segment);
916 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
920 macros
.unit
= currency
; // NOLINT
924 blueprint_helpers::generateCurrencyOption(const CurrencyUnit
& currency
, UnicodeString
& sb
, UErrorCode
&) {
925 sb
.append(currency
.getISOCurrency(), -1);
928 void blueprint_helpers::parseMeasureUnitOption(const StringSegment
& segment
, MacroProps
& macros
,
929 UErrorCode
& status
) {
930 const UnicodeString stemString
= segment
.toTempUnicodeString();
932 // NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
933 // http://unicode.org/reports/tr35/#Validity_Data
935 while (firstHyphen
< stemString
.length() && stemString
.charAt(firstHyphen
) != '-') {
938 if (firstHyphen
== stemString
.length()) {
939 // throw new SkeletonSyntaxException("Invalid measure unit option", segment);
940 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
944 // Need to do char <-> UChar conversion...
945 U_ASSERT(U_SUCCESS(status
));
947 SKELETON_UCHAR_TO_CHAR(type
, stemString
, 0, firstHyphen
, status
);
949 SKELETON_UCHAR_TO_CHAR(subType
, stemString
, firstHyphen
+ 1, stemString
.length(), status
);
951 // Note: the largest type as of this writing (March 2018) is "volume", which has 24 units.
952 static constexpr int32_t CAPACITY
= 30;
953 MeasureUnit units
[CAPACITY
];
954 UErrorCode localStatus
= U_ZERO_ERROR
;
955 int32_t numUnits
= MeasureUnit::getAvailable(type
.data(), units
, CAPACITY
, localStatus
);
956 if (U_FAILURE(localStatus
)) {
957 // More than 30 units in this type?
958 status
= U_INTERNAL_PROGRAM_ERROR
;
961 for (int32_t i
= 0; i
< numUnits
; i
++) {
962 auto& unit
= units
[i
];
963 if (uprv_strcmp(subType
.data(), unit
.getSubtype()) == 0) {
969 // throw new SkeletonSyntaxException("Unknown measure unit", segment);
970 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
973 void blueprint_helpers::generateMeasureUnitOption(const MeasureUnit
& measureUnit
, UnicodeString
& sb
,
975 // Need to do char <-> UChar conversion...
976 sb
.append(UnicodeString(measureUnit
.getType(), -1, US_INV
));
978 sb
.append(UnicodeString(measureUnit
.getSubtype(), -1, US_INV
));
981 void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment
& segment
, MacroProps
& macros
,
982 UErrorCode
& status
) {
983 // A little bit of a hack: safe the current unit (numerator), call the main measure unit
984 // parsing code, put back the numerator unit, and put the new unit into per-unit.
985 MeasureUnit numerator
= macros
.unit
;
986 parseMeasureUnitOption(segment
, macros
, status
);
987 if (U_FAILURE(status
)) { return; }
988 macros
.perUnit
= macros
.unit
;
989 macros
.unit
= numerator
;
992 void blueprint_helpers::parseFractionStem(const StringSegment
& segment
, MacroProps
& macros
,
993 UErrorCode
& status
) {
994 U_ASSERT(segment
.charAt(0) == u
'.');
998 for (; offset
< segment
.length(); offset
++) {
999 if (segment
.charAt(offset
) == u
'0') {
1005 if (offset
< segment
.length()) {
1006 if (segment
.charAt(offset
) == u
'+') {
1011 for (; offset
< segment
.length(); offset
++) {
1012 if (segment
.charAt(offset
) == u
'#') {
1022 if (offset
< segment
.length()) {
1023 // throw new SkeletonSyntaxException("Invalid fraction stem", segment);
1024 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1027 // Use the public APIs to enforce bounds checking
1028 if (maxFrac
== -1) {
1029 macros
.precision
= Precision::minFraction(minFrac
);
1031 macros
.precision
= Precision::minMaxFraction(minFrac
, maxFrac
);
1036 blueprint_helpers::generateFractionStem(int32_t minFrac
, int32_t maxFrac
, UnicodeString
& sb
, UErrorCode
&) {
1037 if (minFrac
== 0 && maxFrac
== 0) {
1038 sb
.append(u
"precision-integer", -1);
1042 appendMultiple(sb
, u
'0', minFrac
);
1043 if (maxFrac
== -1) {
1046 appendMultiple(sb
, u
'#', maxFrac
- minFrac
);
1051 blueprint_helpers::parseDigitsStem(const StringSegment
& segment
, MacroProps
& macros
, UErrorCode
& status
) {
1052 U_ASSERT(segment
.charAt(0) == u
'@');
1056 for (; offset
< segment
.length(); offset
++) {
1057 if (segment
.charAt(offset
) == u
'@') {
1063 if (offset
< segment
.length()) {
1064 if (segment
.charAt(offset
) == u
'+') {
1069 for (; offset
< segment
.length(); offset
++) {
1070 if (segment
.charAt(offset
) == u
'#') {
1080 if (offset
< segment
.length()) {
1081 // throw new SkeletonSyntaxException("Invalid significant digits stem", segment);
1082 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1085 // Use the public APIs to enforce bounds checking
1087 macros
.precision
= Precision::minSignificantDigits(minSig
);
1089 macros
.precision
= Precision::minMaxSignificantDigits(minSig
, maxSig
);
1094 blueprint_helpers::generateDigitsStem(int32_t minSig
, int32_t maxSig
, UnicodeString
& sb
, UErrorCode
&) {
1095 appendMultiple(sb
, u
'@', minSig
);
1099 appendMultiple(sb
, u
'#', maxSig
- minSig
);
1103 bool blueprint_helpers::parseFracSigOption(const StringSegment
& segment
, MacroProps
& macros
,
1104 UErrorCode
& status
) {
1105 if (segment
.charAt(0) != u
'@') {
1111 for (; offset
< segment
.length(); offset
++) {
1112 if (segment
.charAt(offset
) == u
'@') {
1118 // For the frac-sig option, there must be minSig or maxSig but not both.
1119 // Valid: @+, @@+, @@@+
1120 // Valid: @#, @##, @###
1121 // Invalid: @, @@, @@@
1122 // Invalid: @@#, @@##, @@@#
1123 if (offset
< segment
.length()) {
1124 if (segment
.charAt(offset
) == u
'+') {
1127 } else if (minSig
> 1) {
1129 // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
1130 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1134 for (; offset
< segment
.length(); offset
++) {
1135 if (segment
.charAt(offset
) == u
'#') {
1144 // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
1145 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1148 if (offset
< segment
.length()) {
1149 // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
1150 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1154 auto& oldPrecision
= static_cast<const FractionPrecision
&>(macros
.precision
);
1156 macros
.precision
= oldPrecision
.withMinDigits(minSig
);
1158 macros
.precision
= oldPrecision
.withMaxDigits(maxSig
);
1163 void blueprint_helpers::parseIncrementOption(const StringSegment
& segment
, MacroProps
& macros
,
1164 UErrorCode
& status
) {
1165 // Need to do char <-> UChar conversion...
1166 U_ASSERT(U_SUCCESS(status
));
1168 SKELETON_UCHAR_TO_CHAR(buffer
, segment
.toTempUnicodeString(), 0, segment
.length(), status
);
1170 // Utilize DecimalQuantity/decNumber to parse this for us.
1172 UErrorCode localStatus
= U_ZERO_ERROR
;
1173 dq
.setToDecNumber({buffer
.data(), buffer
.length()}, localStatus
);
1174 if (U_FAILURE(localStatus
)) {
1175 // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
1176 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1179 double increment
= dq
.toDouble();
1181 // We also need to figure out how many digits. Do a brute force string operation.
1182 int decimalOffset
= 0;
1183 while (decimalOffset
< segment
.length() && segment
.charAt(decimalOffset
) != '.') {
1186 if (decimalOffset
== segment
.length()) {
1187 macros
.precision
= Precision::increment(increment
);
1189 int32_t fractionLength
= segment
.length() - decimalOffset
- 1;
1190 macros
.precision
= Precision::increment(increment
).withMinFraction(fractionLength
);
1194 void blueprint_helpers::generateIncrementOption(double increment
, int32_t trailingZeros
, UnicodeString
& sb
,
1196 // Utilize DecimalQuantity/double_conversion to format this for us.
1198 dq
.setToDouble(increment
);
1199 dq
.roundToInfinity();
1200 sb
.append(dq
.toPlainString());
1202 // We might need to append extra trailing zeros for min fraction...
1203 if (trailingZeros
> 0) {
1204 appendMultiple(sb
, u
'0', trailingZeros
);
1208 void blueprint_helpers::parseIntegerWidthOption(const StringSegment
& segment
, MacroProps
& macros
,
1209 UErrorCode
& status
) {
1213 if (segment
.charAt(0) == u
'+') {
1219 for (; offset
< segment
.length(); offset
++) {
1220 if (segment
.charAt(offset
) == u
'#') {
1226 if (offset
< segment
.length()) {
1227 for (; offset
< segment
.length(); offset
++) {
1228 if (segment
.charAt(offset
) == u
'0') {
1238 if (offset
< segment
.length()) {
1239 // throw new SkeletonSyntaxException("Invalid integer width stem", segment);
1240 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1243 // Use the public APIs to enforce bounds checking
1245 macros
.integerWidth
= IntegerWidth::zeroFillTo(minInt
);
1247 macros
.integerWidth
= IntegerWidth::zeroFillTo(minInt
).truncateAt(maxInt
);
1251 void blueprint_helpers::generateIntegerWidthOption(int32_t minInt
, int32_t maxInt
, UnicodeString
& sb
,
1256 appendMultiple(sb
, u
'#', maxInt
- minInt
);
1258 appendMultiple(sb
, u
'0', minInt
);
1261 void blueprint_helpers::parseNumberingSystemOption(const StringSegment
& segment
, MacroProps
& macros
,
1262 UErrorCode
& status
) {
1263 // Need to do char <-> UChar conversion...
1264 U_ASSERT(U_SUCCESS(status
));
1266 SKELETON_UCHAR_TO_CHAR(buffer
, segment
.toTempUnicodeString(), 0, segment
.length(), status
);
1268 NumberingSystem
* ns
= NumberingSystem::createInstanceByName(buffer
.data(), status
);
1269 if (ns
== nullptr || U_FAILURE(status
)) {
1270 // This is a skeleton syntax error; don't bubble up the low-level NumberingSystem error
1271 // throw new SkeletonSyntaxException("Unknown numbering system", segment);
1272 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1275 macros
.symbols
.setTo(ns
);
1278 void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem
& ns
, UnicodeString
& sb
,
1280 // Need to do char <-> UChar conversion...
1281 sb
.append(UnicodeString(ns
.getName(), -1, US_INV
));
1284 void blueprint_helpers::parseScaleOption(const StringSegment
& segment
, MacroProps
& macros
,
1285 UErrorCode
& status
) {
1286 // Need to do char <-> UChar conversion...
1287 U_ASSERT(U_SUCCESS(status
));
1289 SKELETON_UCHAR_TO_CHAR(buffer
, segment
.toTempUnicodeString(), 0, segment
.length(), status
);
1291 LocalPointer
<DecNum
> decnum(new DecNum(), status
);
1292 if (U_FAILURE(status
)) { return; }
1293 decnum
->setTo({buffer
.data(), buffer
.length()}, status
);
1294 if (U_FAILURE(status
)) {
1295 // This is a skeleton syntax error; don't let the low-level decnum error bubble up
1296 status
= U_NUMBER_SKELETON_SYNTAX_ERROR
;
1300 // NOTE: The constructor will optimize the decnum for us if possible.
1301 macros
.scale
= {0, decnum
.orphan()};
1304 void blueprint_helpers::generateScaleOption(int32_t magnitude
, const DecNum
* arbitrary
, UnicodeString
& sb
,
1305 UErrorCode
& status
) {
1306 // Utilize DecimalQuantity/double_conversion to format this for us.
1308 if (arbitrary
!= nullptr) {
1309 dq
.setToDecNum(*arbitrary
, status
);
1310 if (U_FAILURE(status
)) { return; }
1314 dq
.adjustMagnitude(magnitude
);
1315 dq
.roundToInfinity();
1316 sb
.append(dq
.toPlainString());
1320 bool GeneratorHelpers::notation(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1321 if (macros
.notation
.fType
== Notation::NTN_COMPACT
) {
1322 UNumberCompactStyle style
= macros
.notation
.fUnion
.compactStyle
;
1323 if (style
== UNumberCompactStyle::UNUM_LONG
) {
1324 sb
.append(u
"compact-long", -1);
1326 } else if (style
== UNumberCompactStyle::UNUM_SHORT
) {
1327 sb
.append(u
"compact-short", -1);
1330 // Compact notation generated from custom data (not supported in skeleton)
1331 // The other compact notations are literals
1332 status
= U_UNSUPPORTED_ERROR
;
1335 } else if (macros
.notation
.fType
== Notation::NTN_SCIENTIFIC
) {
1336 const Notation::ScientificSettings
& impl
= macros
.notation
.fUnion
.scientific
;
1337 if (impl
.fEngineeringInterval
== 3) {
1338 sb
.append(u
"engineering", -1);
1340 sb
.append(u
"scientific", -1);
1342 if (impl
.fMinExponentDigits
> 1) {
1344 blueprint_helpers::generateExponentWidthOption(impl
.fMinExponentDigits
, sb
, status
);
1345 if (U_FAILURE(status
)) {
1349 if (impl
.fExponentSignDisplay
!= UNUM_SIGN_AUTO
) {
1351 enum_to_stem_string::signDisplay(impl
.fExponentSignDisplay
, sb
);
1355 // Default value is not shown in normalized form
1360 bool GeneratorHelpers::unit(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1361 if (utils::unitIsCurrency(macros
.unit
)) {
1362 sb
.append(u
"currency/", -1);
1363 CurrencyUnit
currency(macros
.unit
, status
);
1364 if (U_FAILURE(status
)) {
1367 blueprint_helpers::generateCurrencyOption(currency
, sb
, status
);
1369 } else if (utils::unitIsNoUnit(macros
.unit
)) {
1370 if (utils::unitIsPercent(macros
.unit
)) {
1371 sb
.append(u
"percent", -1);
1373 } else if (utils::unitIsPermille(macros
.unit
)) {
1374 sb
.append(u
"permille", -1);
1377 // Default value is not shown in normalized form
1381 sb
.append(u
"measure-unit/", -1);
1382 blueprint_helpers::generateMeasureUnitOption(macros
.unit
, sb
, status
);
1387 bool GeneratorHelpers::perUnit(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1388 // Per-units are currently expected to be only MeasureUnits.
1389 if (utils::unitIsNoUnit(macros
.perUnit
)) {
1390 if (utils::unitIsPercent(macros
.perUnit
) || utils::unitIsPermille(macros
.perUnit
)) {
1391 status
= U_UNSUPPORTED_ERROR
;
1394 // Default value: ok to ignore
1397 } else if (utils::unitIsCurrency(macros
.perUnit
)) {
1398 status
= U_UNSUPPORTED_ERROR
;
1401 sb
.append(u
"per-measure-unit/", -1);
1402 blueprint_helpers::generateMeasureUnitOption(macros
.perUnit
, sb
, status
);
1407 bool GeneratorHelpers::precision(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1408 if (macros
.precision
.fType
== Precision::RND_NONE
) {
1409 sb
.append(u
"precision-unlimited", -1);
1410 } else if (macros
.precision
.fType
== Precision::RND_FRACTION
) {
1411 const Precision::FractionSignificantSettings
& impl
= macros
.precision
.fUnion
.fracSig
;
1412 blueprint_helpers::generateFractionStem(impl
.fMinFrac
, impl
.fMaxFrac
, sb
, status
);
1413 } else if (macros
.precision
.fType
== Precision::RND_SIGNIFICANT
) {
1414 const Precision::FractionSignificantSettings
& impl
= macros
.precision
.fUnion
.fracSig
;
1415 blueprint_helpers::generateDigitsStem(impl
.fMinSig
, impl
.fMaxSig
, sb
, status
);
1416 } else if (macros
.precision
.fType
== Precision::RND_INCREMENT_SIGNIFICANT
) { // Apple rdar://52538227
1417 const Precision::IncrementSignificantSettings
& impl
= macros
.precision
.fUnion
.incrSig
;
1418 blueprint_helpers::generateDigitsStem(impl
.fMinSig
, impl
.fMaxSig
, sb
, status
);
1419 } else if (macros
.precision
.fType
== Precision::RND_FRACTION_SIGNIFICANT
) {
1420 const Precision::FractionSignificantSettings
& impl
= macros
.precision
.fUnion
.fracSig
;
1421 blueprint_helpers::generateFractionStem(impl
.fMinFrac
, impl
.fMaxFrac
, sb
, status
);
1423 if (impl
.fMinSig
== -1) {
1424 blueprint_helpers::generateDigitsStem(1, impl
.fMaxSig
, sb
, status
);
1426 blueprint_helpers::generateDigitsStem(impl
.fMinSig
, -1, sb
, status
);
1428 } else if (macros
.precision
.fType
== Precision::RND_INCREMENT
1429 || macros
.precision
.fType
== Precision::RND_INCREMENT_ONE
1430 || macros
.precision
.fType
== Precision::RND_INCREMENT_FIVE
) {
1431 const Precision::IncrementSettings
& impl
= macros
.precision
.fUnion
.increment
;
1432 sb
.append(u
"precision-increment/", -1);
1433 blueprint_helpers::generateIncrementOption(
1435 impl
.fMinFrac
- impl
.fMaxFrac
,
1438 } else if (macros
.precision
.fType
== Precision::RND_CURRENCY
) {
1439 UCurrencyUsage usage
= macros
.precision
.fUnion
.currencyUsage
;
1440 if (usage
== UCURR_USAGE_STANDARD
) {
1441 sb
.append(u
"precision-currency-standard", -1);
1443 sb
.append(u
"precision-currency-cash", -1);
1450 // NOTE: Always return true for rounding because the default value depends on other options.
1454 bool GeneratorHelpers::roundingMode(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1455 if (macros
.roundingMode
== kDefaultMode
) {
1456 return false; // Default
1458 enum_to_stem_string::roundingMode(macros
.roundingMode
, sb
);
1462 bool GeneratorHelpers::grouping(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1463 if (macros
.grouper
.isBogus()) {
1464 return false; // No value
1465 } else if (macros
.grouper
.fStrategy
== UNUM_GROUPING_COUNT
) {
1466 status
= U_UNSUPPORTED_ERROR
;
1468 } else if (macros
.grouper
.fStrategy
== UNUM_GROUPING_AUTO
) {
1469 return false; // Default value
1471 enum_to_stem_string::groupingStrategy(macros
.grouper
.fStrategy
, sb
);
1476 bool GeneratorHelpers::integerWidth(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1477 if (macros
.integerWidth
.fHasError
|| macros
.integerWidth
.isBogus() ||
1478 macros
.integerWidth
== IntegerWidth::standard()) {
1482 sb
.append(u
"integer-width/", -1);
1483 blueprint_helpers::generateIntegerWidthOption(
1484 macros
.integerWidth
.fUnion
.minMaxInt
.fMinInt
,
1485 macros
.integerWidth
.fUnion
.minMaxInt
.fMaxInt
,
1491 bool GeneratorHelpers::symbols(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1492 if (macros
.symbols
.isNumberingSystem()) {
1493 const NumberingSystem
& ns
= *macros
.symbols
.getNumberingSystem();
1494 if (uprv_strcmp(ns
.getName(), "latn") == 0) {
1495 sb
.append(u
"latin", -1);
1497 sb
.append(u
"numbering-system/", -1);
1498 blueprint_helpers::generateNumberingSystemOption(ns
, sb
, status
);
1501 } else if (macros
.symbols
.isDecimalFormatSymbols()) {
1502 status
= U_UNSUPPORTED_ERROR
;
1505 // No custom symbols
1510 bool GeneratorHelpers::unitWidth(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1511 if (macros
.unitWidth
== UNUM_UNIT_WIDTH_SHORT
|| macros
.unitWidth
== UNUM_UNIT_WIDTH_COUNT
) {
1512 return false; // Default or Bogus
1514 enum_to_stem_string::unitWidth(macros
.unitWidth
, sb
);
1518 bool GeneratorHelpers::sign(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1519 if (macros
.sign
== UNUM_SIGN_AUTO
|| macros
.sign
== UNUM_SIGN_COUNT
) {
1520 return false; // Default or Bogus
1522 enum_to_stem_string::signDisplay(macros
.sign
, sb
);
1526 bool GeneratorHelpers::decimal(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
&) {
1527 if (macros
.decimal
== UNUM_DECIMAL_SEPARATOR_AUTO
|| macros
.decimal
== UNUM_DECIMAL_SEPARATOR_COUNT
) {
1528 return false; // Default or Bogus
1530 enum_to_stem_string::decimalSeparatorDisplay(macros
.decimal
, sb
);
1534 bool GeneratorHelpers::scale(const MacroProps
& macros
, UnicodeString
& sb
, UErrorCode
& status
) {
1535 if (!macros
.scale
.isValid()) {
1536 return false; // Default or Bogus
1538 sb
.append(u
"scale/", -1);
1539 blueprint_helpers::generateScaleOption(
1540 macros
.scale
.fMagnitude
,
1541 macros
.scale
.fArbitrary
,
1548 #endif /* #if !UCONFIG_NO_FORMATTING */