mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-09 15:27:38 +00:00
ICU-8610 Dirty commit of C++ work so far. Probably does not build.
X-SVN-Rev: 41142
This commit is contained in:
parent
c725920cff
commit
d8f2d8ce6e
7 changed files with 966 additions and 75 deletions
563
icu4c/source/i18n/number_skeletons.cpp
Normal file
563
icu4c/source/i18n/number_skeletons.cpp
Normal file
|
@ -0,0 +1,563 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
|
||||
|
||||
// Allow implicit conversion from char16_t* to UnicodeString for this file:
|
||||
// Helpful in toString methods and elsewhere.
|
||||
#define UNISTR_FROM_STRING_EXPLICIT
|
||||
|
||||
#include "number_skeletons.h"
|
||||
#include "umutex.h"
|
||||
#include "ucln_in.h"
|
||||
#include "hash.h"
|
||||
#include "patternprops.h"
|
||||
#include "unicode/ucharstriebuilder.h"
|
||||
|
||||
using namespace icu;
|
||||
using namespace icu::number;
|
||||
using namespace icu::number::impl;
|
||||
using namespace icu::number::impl::skeleton;
|
||||
|
||||
static constexpr UErrorCode U_NUMBER_SKELETON_SYNTAX_ERROR = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
namespace {
|
||||
|
||||
icu::UInitOnce gNumberSkeletonsInitOnce = U_INITONCE_INITIALIZER;
|
||||
|
||||
char16_t* kSerializedStemTrie = nullptr;
|
||||
|
||||
UBool U_CALLCONV cleanupNumberSkeletons() {
|
||||
uprv_free(kSerializedStemTrie);
|
||||
kSerializedStemTrie = nullptr;
|
||||
}
|
||||
|
||||
void U_CALLCONV initNumberSkeletons(UErrorCode& status) {
|
||||
ucln_i18n_registerCleanup(UCLN_I18N_NUMBER_SKELETONS, cleanupNumberSkeletons);
|
||||
|
||||
UCharsTrieBuilder b(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
// Section 1:
|
||||
b.add(u"compact-short", STEM_COMPACT_SHORT, status);
|
||||
b.add(u"compact-long", STEM_COMPACT_LONG, status);
|
||||
b.add(u"scientific", STEM_SCIENTIFIC, status);
|
||||
b.add(u"engineering", STEM_ENGINEERING, status);
|
||||
b.add(u"notation-simple", STEM_NOTATION_SIMPLE, status);
|
||||
b.add(u"base-unit", STEM_BASE_UNIT, status);
|
||||
b.add(u"percent", STEM_PERCENT, status);
|
||||
b.add(u"permille", STEM_PERMILLE, status);
|
||||
b.add(u"round-integer", STEM_ROUND_INTEGER, status);
|
||||
b.add(u"round-unlimited", STEM_ROUND_UNLIMITED, status);
|
||||
b.add(u"round-currency-standard", STEM_ROUND_CURRENCY_STANDARD, status);
|
||||
b.add(u"round-currency-cash", STEM_ROUND_CURRENCY_CASH, status);
|
||||
b.add(u"group-off", STEM_GROUP_OFF, status);
|
||||
b.add(u"group-min2", STEM_GROUP_MIN2, status);
|
||||
b.add(u"group-auto", STEM_GROUP_AUTO, status);
|
||||
b.add(u"group-on-aligned", STEM_GROUP_ON_ALIGNED, status);
|
||||
b.add(u"group-thousands", STEM_GROUP_THOUSANDS, status);
|
||||
b.add(u"latin", STEM_LATIN, status);
|
||||
b.add(u"unit-width-narrow", STEM_UNIT_WIDTH_NARROW, status);
|
||||
b.add(u"unit-width-short", STEM_UNIT_WIDTH_SHORT, status);
|
||||
b.add(u"unit-width-full-name", STEM_UNIT_WIDTH_FULL_NAME, status);
|
||||
b.add(u"unit-width-iso-code", STEM_UNIT_WIDTH_ISO_CODE, status);
|
||||
b.add(u"unit-width-hidden", STEM_UNIT_WIDTH_HIDDEN, status);
|
||||
b.add(u"sign-auto", STEM_SIGN_AUTO, status);
|
||||
b.add(u"sign-always", STEM_SIGN_ALWAYS, status);
|
||||
b.add(u"sign-never", STEM_SIGN_NEVER, status);
|
||||
b.add(u"sign-accounting", STEM_SIGN_ACCOUNTING, status);
|
||||
b.add(u"sign-accounting-always", STEM_SIGN_ACCOUNTING_ALWAYS, status);
|
||||
b.add(u"sign-except-zero", STEM_SIGN_EXCEPT_ZERO, status);
|
||||
b.add(u"sign-accounting-except-zero", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status);
|
||||
b.add(u"decimal-auto", STEM_DECIMAL_AUTO, status);
|
||||
b.add(u"decimal-always", STEM_DECIMAL_ALWAYS, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
// Section 2:
|
||||
b.add(u"round-increment", STEM_ROUND_INCREMENT, status);
|
||||
b.add(u"measure-unit", STEM_MEASURE_UNIT, status);
|
||||
b.add(u"per-measure-unit", STEM_PER_MEASURE_UNIT, status);
|
||||
b.add(u"currency", STEM_CURRENCY, status);
|
||||
b.add(u"integer-width", STEM_INTEGER_WIDTH, status);
|
||||
b.add(u"numbering-system", STEM_NUMBERING_SYSTEM, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
// Build the CharsTrie
|
||||
// TODO: Use SLOW or FAST here?
|
||||
UnicodeString result;
|
||||
b.buildUnicodeString(USTRINGTRIE_BUILD_FAST, result, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
// Copy the result into the global constant pointer
|
||||
size_t numBytes = result.length() * sizeof(char16_t);
|
||||
kSerializedStemTrie = static_cast<char16_t*>(uprv_malloc(numBytes));
|
||||
uprv_memcpy(kSerializedStemTrie, result.getBuffer(), numBytes);
|
||||
}
|
||||
|
||||
|
||||
#define CHECK_NULL(seen, field, status) void; /* for auto-format line wraping */ \
|
||||
{ \
|
||||
if ((seen).field) { \
|
||||
(status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
|
||||
return STATE_NULL; \
|
||||
} \
|
||||
(seen).field = true; \
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
Notation stem_to_object::notation(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_COMPACT_SHORT:
|
||||
return Notation::compactShort();
|
||||
case STEM_COMPACT_LONG:
|
||||
return Notation::compactLong();
|
||||
case STEM_SCIENTIFIC:
|
||||
return Notation::scientific();
|
||||
case STEM_ENGINEERING:
|
||||
return Notation::engineering();
|
||||
case STEM_NOTATION_SIMPLE:
|
||||
return Notation::simple();
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_BASE_UNIT:
|
||||
// Slicing is okay
|
||||
return NoUnit::base(); // NOLINT
|
||||
case STEM_PERCENT:
|
||||
// Slicing is okay
|
||||
return NoUnit::percent(); // NOLINT
|
||||
case STEM_PERMILLE:
|
||||
// Slicing is okay
|
||||
return NoUnit::permille(); // NOLINT
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
Rounder stem_to_object::rounder(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_ROUND_INTEGER:
|
||||
return Rounder::integer();
|
||||
case STEM_ROUND_UNLIMITED:
|
||||
return Rounder::unlimited();
|
||||
case STEM_ROUND_CURRENCY_STANDARD:
|
||||
return Rounder::currency(UCURR_USAGE_STANDARD);
|
||||
case STEM_ROUND_CURRENCY_CASH:
|
||||
return Rounder::currency(UCURR_USAGE_CASH);
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
UGroupingStrategy stem_to_object::groupingStrategy(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_GROUP_OFF:
|
||||
return UNUM_GROUPING_OFF;
|
||||
case STEM_GROUP_MIN2:
|
||||
return UNUM_GROUPING_MIN2;
|
||||
case STEM_GROUP_AUTO:
|
||||
return UNUM_GROUPING_AUTO;
|
||||
case STEM_GROUP_ON_ALIGNED:
|
||||
return UNUM_GROUPING_ON_ALIGNED;
|
||||
case STEM_GROUP_THOUSANDS:
|
||||
return UNUM_GROUPING_THOUSANDS;
|
||||
default:
|
||||
return UNUM_GROUPING_COUNT; // for objects, throw; for enums, return COUNT
|
||||
}
|
||||
}
|
||||
|
||||
UNumberUnitWidth stem_to_object::unitWidth(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_UNIT_WIDTH_NARROW:
|
||||
return UNUM_UNIT_WIDTH_NARROW;
|
||||
case STEM_UNIT_WIDTH_SHORT:
|
||||
return UNUM_UNIT_WIDTH_SHORT;
|
||||
case STEM_UNIT_WIDTH_FULL_NAME:
|
||||
return UNUM_UNIT_WIDTH_FULL_NAME;
|
||||
case STEM_UNIT_WIDTH_ISO_CODE:
|
||||
return UNUM_UNIT_WIDTH_ISO_CODE;
|
||||
case STEM_UNIT_WIDTH_HIDDEN:
|
||||
return UNUM_UNIT_WIDTH_HIDDEN;
|
||||
default:
|
||||
return UNUM_UNIT_WIDTH_COUNT; // for objects, throw; for enums, return COUNT
|
||||
}
|
||||
}
|
||||
|
||||
UNumberSignDisplay stem_to_object::signDisplay(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_SIGN_AUTO:
|
||||
return UNUM_SIGN_AUTO;
|
||||
case STEM_SIGN_ALWAYS:
|
||||
return UNUM_SIGN_ALWAYS;
|
||||
case STEM_SIGN_NEVER:
|
||||
return UNUM_SIGN_NEVER;
|
||||
case STEM_SIGN_ACCOUNTING:
|
||||
return UNUM_SIGN_ACCOUNTING;
|
||||
case STEM_SIGN_ACCOUNTING_ALWAYS:
|
||||
return UNUM_SIGN_ACCOUNTING_ALWAYS;
|
||||
case STEM_SIGN_EXCEPT_ZERO:
|
||||
return UNUM_SIGN_EXCEPT_ZERO;
|
||||
case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO:
|
||||
return UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
|
||||
default:
|
||||
return UNUM_SIGN_COUNT; // for objects, throw; for enums, return COUNT
|
||||
}
|
||||
}
|
||||
|
||||
UNumberDecimalSeparatorDisplay stem_to_object::decimalSeparatorDisplay(skeleton::StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_DECIMAL_AUTO:
|
||||
return UNUM_DECIMAL_SEPARATOR_AUTO;
|
||||
case STEM_DECIMAL_ALWAYS:
|
||||
return UNUM_DECIMAL_SEPARATOR_ALWAYS;
|
||||
default:
|
||||
return UNUM_DECIMAL_SEPARATOR_COUNT; // for objects, throw; for enums, return COUNT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void enum_to_stem_string::groupingStrategy(UGroupingStrategy value, UnicodeString& sb) {
|
||||
switch (value) {
|
||||
case UNUM_GROUPING_OFF:
|
||||
sb.append(u"group-off", -1);
|
||||
break;
|
||||
case UNUM_GROUPING_MIN2:
|
||||
sb.append(u"group-min2", -1);
|
||||
break;
|
||||
case UNUM_GROUPING_AUTO:
|
||||
sb.append(u"group-auto", -1);
|
||||
break;
|
||||
case UNUM_GROUPING_ON_ALIGNED:
|
||||
sb.append(u"group-on-aligned", -1);
|
||||
break;
|
||||
case UNUM_GROUPING_THOUSANDS:
|
||||
sb.append(u"group-thousands", -1);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void enum_to_stem_string::unitWidth(UNumberUnitWidth value, UnicodeString& sb) {
|
||||
switch (value) {
|
||||
case UNUM_UNIT_WIDTH_NARROW:
|
||||
sb.append(u"unit-width-narrow", -1);
|
||||
break;
|
||||
case UNUM_UNIT_WIDTH_SHORT:
|
||||
sb.append(u"unit-width-short", -1);
|
||||
break;
|
||||
case UNUM_UNIT_WIDTH_FULL_NAME:
|
||||
sb.append(u"unit-width-full-name", -1);
|
||||
break;
|
||||
case UNUM_UNIT_WIDTH_ISO_CODE:
|
||||
sb.append(u"unit-width-iso-code", -1);
|
||||
break;
|
||||
case UNUM_UNIT_WIDTH_HIDDEN:
|
||||
sb.append(u"unit-width-hidden", -1);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void enum_to_stem_string::signDisplay(UNumberSignDisplay value, UnicodeString& sb) {
|
||||
switch (value) {
|
||||
case UNUM_SIGN_AUTO:
|
||||
sb.append(u"sign-auto", -1);
|
||||
break;
|
||||
case UNUM_SIGN_ALWAYS:
|
||||
sb.append(u"sign-always", -1);
|
||||
break;
|
||||
case UNUM_SIGN_NEVER:
|
||||
sb.append(u"sign-never", -1);
|
||||
break;
|
||||
case UNUM_SIGN_ACCOUNTING:
|
||||
sb.append(u"sign-accounting", -1);
|
||||
break;
|
||||
case UNUM_SIGN_ACCOUNTING_ALWAYS:
|
||||
sb.append(u"sign-accounting-always", -1);
|
||||
break;
|
||||
case UNUM_SIGN_EXCEPT_ZERO:
|
||||
sb.append(u"sign-except-zero", -1);
|
||||
break;
|
||||
case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO:
|
||||
sb.append(u"sign-accounting-except-zero", -1);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
enum_to_stem_string::decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb) {
|
||||
switch (value) {
|
||||
case UNUM_DECIMAL_SEPARATOR_AUTO:
|
||||
sb.append(u"decimal-auto", -1);
|
||||
break;
|
||||
case UNUM_DECIMAL_SEPARATOR_ALWAYS:
|
||||
sb.append(u"decimal-always", -1);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UnlocalizedNumberFormatter skeleton::create(const UnicodeString& skeletonString, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return {}; }
|
||||
umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status);
|
||||
if (U_FAILURE(status)) { return {}; }
|
||||
|
||||
MacroProps macros = parseSkeleton(skeletonString, status);
|
||||
return NumberFormatter::with().macros(macros);
|
||||
}
|
||||
|
||||
UnicodeString skeleton::generate(const MacroProps& macros, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return {}; }
|
||||
umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status);
|
||||
if (U_FAILURE(status)) { return {}; }
|
||||
|
||||
UnicodeString sb;
|
||||
generateSkeleton(macros, sb, status);
|
||||
return sb;
|
||||
}
|
||||
|
||||
MacroProps skeleton::parseSkeleton(const UnicodeString& skeletonString, UErrorCode& status) {
|
||||
// Add a trailing whitespace to the end of the skeleton string to make code cleaner.
|
||||
UnicodeString tempSkeletonString(skeletonString);
|
||||
tempSkeletonString.append(u' ');
|
||||
|
||||
SeenMacroProps seen;
|
||||
MacroProps macros;
|
||||
StringSegment segment(skeletonString, false);
|
||||
UCharsTrie stemTrie(kSerializedStemTrie);
|
||||
ParseState stem = STATE_NULL;
|
||||
int offset = 0;
|
||||
|
||||
// Primary skeleton parse loop:
|
||||
while (offset < segment.length()) {
|
||||
int cp = segment.codePointAt(offset);
|
||||
bool isTokenSeparator = PatternProps::isWhiteSpace(cp);
|
||||
bool isOptionSeparator = (cp == u'/');
|
||||
|
||||
if (!isTokenSeparator && !isOptionSeparator) {
|
||||
// Non-separator token; consume it.
|
||||
offset += U16_LENGTH(cp);
|
||||
if (stem == STATE_NULL) {
|
||||
// We are currently consuming a stem.
|
||||
// Go to the next state in the stem trie.
|
||||
stemTrie.nextForCodePoint(cp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// We are looking at a token or option separator.
|
||||
// If the segment is nonempty, parse it and reset the segment.
|
||||
// Otherwise, make sure it is a valid repeating separator.
|
||||
if (offset != 0) {
|
||||
segment.setLength(offset);
|
||||
if (stem == STATE_NULL) {
|
||||
// The first separator after the start of a token. Parse it as a stem.
|
||||
stem = parseStem(segment, stemTrie, seen, macros, status);
|
||||
stemTrie.reset();
|
||||
} else {
|
||||
// A separator after the first separator of a token. Parse it as an option.
|
||||
stem = parseOption(stem, segment, macros, status);
|
||||
}
|
||||
segment.resetLength();
|
||||
|
||||
// Consume the segment:
|
||||
segment.adjustOffset(offset);
|
||||
offset = 0;
|
||||
|
||||
} else if (stem != STATE_NULL) {
|
||||
// A separator ('/' or whitespace) following an option separator ('/')
|
||||
// segment.setLength(U16_LENGTH(cp)); // for error message
|
||||
// throw new SkeletonSyntaxException("Unexpected separator character", segment);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return macros;
|
||||
|
||||
} else {
|
||||
// Two spaces in a row; this is OK.
|
||||
}
|
||||
|
||||
// Does the current stem forbid options?
|
||||
if (isOptionSeparator && stem == STATE_NULL) {
|
||||
// segment.setLength(U16_LENGTH(cp)); // for error message
|
||||
// throw new SkeletonSyntaxException("Unexpected option separator", segment);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return macros;
|
||||
}
|
||||
|
||||
// Does the current stem require an option?
|
||||
if (isTokenSeparator && stem != STATE_NULL) {
|
||||
switch (stem) {
|
||||
case STATE_INCREMENT_ROUNDER:
|
||||
case STATE_MEASURE_UNIT:
|
||||
case STATE_PER_MEASURE_UNIT:
|
||||
case STATE_CURRENCY_UNIT:
|
||||
case STATE_INTEGER_WIDTH:
|
||||
case STATE_NUMBERING_SYSTEM:
|
||||
// segment.setLength(U16_LENGTH(cp)); // for error message
|
||||
// throw new SkeletonSyntaxException("Stem requires an option", segment);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return macros;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
stem = STATE_NULL;
|
||||
}
|
||||
|
||||
// Consume the separator:
|
||||
segment.adjustOffset(U16_LENGTH(cp));
|
||||
}
|
||||
U_ASSERT(stem == STATE_NULL);
|
||||
return macros;
|
||||
}
|
||||
|
||||
ParseState
|
||||
skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen,
|
||||
MacroProps& macros, UErrorCode& status) {
|
||||
// First check for "blueprint" stems, which start with a "signal char"
|
||||
switch (segment.charAt(0)) {
|
||||
case u'.':
|
||||
CHECK_NULL(seen, rounder, status);
|
||||
blueprint_helpers::parseFractionStem(segment, macros, status);
|
||||
return STATE_FRACTION_ROUNDER;
|
||||
case u'@':
|
||||
CHECK_NULL(seen, rounder, status);
|
||||
blueprint_helpers::parseDigitsStem(segment, macros, status);
|
||||
return STATE_NULL;
|
||||
}
|
||||
|
||||
// Now look at the stemsTrie, which is already be pointing at our stem.
|
||||
UStringTrieResult stemResult = stemTrie.current();
|
||||
|
||||
if (stemResult != USTRINGTRIE_INTERMEDIATE_VALUE && stemResult != USTRINGTRIE_FINAL_VALUE) {
|
||||
// throw new SkeletonSyntaxException("Unknown stem", segment);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return STATE_NULL;
|
||||
}
|
||||
|
||||
auto stem = static_cast<StemEnum>(stemTrie.getValue());
|
||||
switch (stem) {
|
||||
|
||||
// Stems with meaning on their own, not requiring an option:
|
||||
|
||||
case STEM_COMPACT_SHORT:
|
||||
case STEM_COMPACT_LONG:
|
||||
case STEM_SCIENTIFIC:
|
||||
case STEM_ENGINEERING:
|
||||
case STEM_NOTATION_SIMPLE:
|
||||
CHECK_NULL(seen, notation, status);
|
||||
macros.notation = stem_to_object::notation(stem);
|
||||
switch (stem) {
|
||||
case STEM_SCIENTIFIC:
|
||||
case STEM_ENGINEERING:
|
||||
return STATE_SCIENTIFIC; // allows for scientific options
|
||||
default:
|
||||
return STATE_NULL;
|
||||
}
|
||||
|
||||
case STEM_BASE_UNIT:
|
||||
case STEM_PERCENT:
|
||||
case STEM_PERMILLE:
|
||||
CHECK_NULL(seen, unit, status);
|
||||
macros.unit = stem_to_object::unit(stem);
|
||||
return STATE_NULL;
|
||||
|
||||
case STEM_ROUND_INTEGER:
|
||||
case STEM_ROUND_UNLIMITED:
|
||||
case STEM_ROUND_CURRENCY_STANDARD:
|
||||
case STEM_ROUND_CURRENCY_CASH:
|
||||
CHECK_NULL(seen, rounder, status);
|
||||
macros.rounder = stem_to_object::rounder(stem);
|
||||
switch (stem) {
|
||||
case STEM_ROUND_INTEGER:
|
||||
return STATE_FRACTION_ROUNDER; // allows for "round-integer/@##"
|
||||
default:
|
||||
return STATE_ROUNDER; // allows for rounding mode options
|
||||
}
|
||||
|
||||
case STEM_GROUP_OFF:
|
||||
case STEM_GROUP_MIN2:
|
||||
case STEM_GROUP_AUTO:
|
||||
case STEM_GROUP_ON_ALIGNED:
|
||||
case STEM_GROUP_THOUSANDS:
|
||||
CHECK_NULL(seen, grouper, status);
|
||||
macros.grouper = Grouper::forStrategy(stem_to_object::groupingStrategy(stem));
|
||||
return STATE_NULL;
|
||||
|
||||
case STEM_LATIN:
|
||||
CHECK_NULL(seen, symbols, status);
|
||||
macros.symbols.setTo(NumberingSystem::createInstanceByName("latn", status));
|
||||
return STATE_NULL;
|
||||
|
||||
case STEM_UNIT_WIDTH_NARROW:
|
||||
case STEM_UNIT_WIDTH_SHORT:
|
||||
case STEM_UNIT_WIDTH_FULL_NAME:
|
||||
case STEM_UNIT_WIDTH_ISO_CODE:
|
||||
case STEM_UNIT_WIDTH_HIDDEN:
|
||||
CHECK_NULL(seen, unitWidth, status);
|
||||
macros.unitWidth = stem_to_object::unitWidth(stem);
|
||||
return STATE_NULL;
|
||||
|
||||
case STEM_SIGN_AUTO:
|
||||
case STEM_SIGN_ALWAYS:
|
||||
case STEM_SIGN_NEVER:
|
||||
case STEM_SIGN_ACCOUNTING:
|
||||
case STEM_SIGN_ACCOUNTING_ALWAYS:
|
||||
case STEM_SIGN_EXCEPT_ZERO:
|
||||
case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO:
|
||||
CHECK_NULL(seen, sign, status);
|
||||
macros.sign = stem_to_object::signDisplay(stem);
|
||||
return STATE_NULL;
|
||||
|
||||
case STEM_DECIMAL_AUTO:
|
||||
case STEM_DECIMAL_ALWAYS:
|
||||
CHECK_NULL(seen, decimal, status);
|
||||
macros.decimal = stem_to_object::decimalSeparatorDisplay(stem);
|
||||
return STATE_NULL;
|
||||
|
||||
// Stems requiring an option:
|
||||
|
||||
case STEM_ROUND_INCREMENT:
|
||||
CHECK_NULL(seen, rounder, status);
|
||||
return STATE_INCREMENT_ROUNDER;
|
||||
|
||||
case STEM_MEASURE_UNIT:
|
||||
CHECK_NULL(seen, unit, status);
|
||||
return STATE_MEASURE_UNIT;
|
||||
|
||||
case STEM_PER_MEASURE_UNIT:
|
||||
CHECK_NULL(seen, perUnit, status);
|
||||
return STATE_PER_MEASURE_UNIT;
|
||||
|
||||
case STEM_CURRENCY:
|
||||
CHECK_NULL(seen, unit, status);
|
||||
return STATE_CURRENCY_UNIT;
|
||||
|
||||
case STEM_INTEGER_WIDTH:
|
||||
CHECK_NULL(seen, integerWidth, status);
|
||||
return STATE_INTEGER_WIDTH;
|
||||
|
||||
case STEM_NUMBERING_SYSTEM:
|
||||
CHECK_NULL(seen, symbols, status);
|
||||
return STATE_NUMBERING_SYSTEM;
|
||||
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
299
icu4c/source/i18n/number_skeletons.h
Normal file
299
icu4c/source/i18n/number_skeletons.h
Normal file
|
@ -0,0 +1,299 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
|
||||
#ifndef __SOURCE_NUMBER_SKELETONS_H__
|
||||
#define __SOURCE_NUMBER_SKELETONS_H__
|
||||
|
||||
#include "number_types.h"
|
||||
#include "numparse_types.h"
|
||||
#include "unicode/ucharstrie.h"
|
||||
|
||||
using icu::numparse::impl::StringSegment;
|
||||
|
||||
U_NAMESPACE_BEGIN namespace number {
|
||||
namespace impl {
|
||||
|
||||
// Forward-declaration
|
||||
struct SeenMacroProps;
|
||||
|
||||
// namespace for enums and entrypoint functions
|
||||
namespace skeleton {
|
||||
|
||||
/**
|
||||
* While parsing a skeleton, this enum records what type of option we expect to find next.
|
||||
*/
|
||||
enum ParseState {
|
||||
|
||||
// Section 0: We expect whitespace or a stem, but not an option:
|
||||
|
||||
STATE_NULL,
|
||||
|
||||
// Section 1: We might accept an option, but it is not required:
|
||||
|
||||
STATE_SCIENTIFIC,
|
||||
STATE_ROUNDER,
|
||||
STATE_FRACTION_ROUNDER,
|
||||
|
||||
// Section 2: An option is required:
|
||||
|
||||
STATE_INCREMENT_ROUNDER,
|
||||
STATE_MEASURE_UNIT,
|
||||
STATE_PER_MEASURE_UNIT,
|
||||
STATE_CURRENCY_UNIT,
|
||||
STATE_INTEGER_WIDTH,
|
||||
STATE_NUMBERING_SYSTEM,
|
||||
};
|
||||
|
||||
/**
|
||||
* All possible stem literals have an entry in the StemEnum. The enum name is the kebab case stem
|
||||
* string literal written in upper snake case.
|
||||
*
|
||||
* @see StemToObject
|
||||
* @see #SERIALIZED_STEM_TRIE
|
||||
*/
|
||||
enum StemEnum {
|
||||
|
||||
// Section 1: Stems that do not require an option:
|
||||
|
||||
STEM_COMPACT_SHORT,
|
||||
STEM_COMPACT_LONG,
|
||||
STEM_SCIENTIFIC,
|
||||
STEM_ENGINEERING,
|
||||
STEM_NOTATION_SIMPLE,
|
||||
STEM_BASE_UNIT,
|
||||
STEM_PERCENT,
|
||||
STEM_PERMILLE,
|
||||
STEM_ROUND_INTEGER,
|
||||
STEM_ROUND_UNLIMITED,
|
||||
STEM_ROUND_CURRENCY_STANDARD,
|
||||
STEM_ROUND_CURRENCY_CASH,
|
||||
STEM_GROUP_OFF,
|
||||
STEM_GROUP_MIN2,
|
||||
STEM_GROUP_AUTO,
|
||||
STEM_GROUP_ON_ALIGNED,
|
||||
STEM_GROUP_THOUSANDS,
|
||||
STEM_LATIN,
|
||||
STEM_UNIT_WIDTH_NARROW,
|
||||
STEM_UNIT_WIDTH_SHORT,
|
||||
STEM_UNIT_WIDTH_FULL_NAME,
|
||||
STEM_UNIT_WIDTH_ISO_CODE,
|
||||
STEM_UNIT_WIDTH_HIDDEN,
|
||||
STEM_SIGN_AUTO,
|
||||
STEM_SIGN_ALWAYS,
|
||||
STEM_SIGN_NEVER,
|
||||
STEM_SIGN_ACCOUNTING,
|
||||
STEM_SIGN_ACCOUNTING_ALWAYS,
|
||||
STEM_SIGN_EXCEPT_ZERO,
|
||||
STEM_SIGN_ACCOUNTING_EXCEPT_ZERO,
|
||||
STEM_DECIMAL_AUTO,
|
||||
STEM_DECIMAL_ALWAYS,
|
||||
|
||||
// Section 2: Stems that DO require an option:
|
||||
|
||||
STEM_ROUND_INCREMENT,
|
||||
STEM_MEASURE_UNIT,
|
||||
STEM_PER_MEASURE_UNIT,
|
||||
STEM_CURRENCY,
|
||||
STEM_INTEGER_WIDTH,
|
||||
STEM_NUMBERING_SYSTEM,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a NumberFormatter corresponding to the given skeleton string.
|
||||
*
|
||||
* @param skeletonString
|
||||
* A number skeleton string, possibly not in its shortest form.
|
||||
* @return An UnlocalizedNumberFormatter with behavior defined by the given skeleton string.
|
||||
*/
|
||||
UnlocalizedNumberFormatter create(const UnicodeString& skeletonString, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Create a skeleton string corresponding to the given NumberFormatter.
|
||||
*
|
||||
* @param macros
|
||||
* The NumberFormatter options object.
|
||||
* @return A skeleton string in normalized form.
|
||||
*/
|
||||
UnicodeString generate(const MacroProps& macros, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Converts from a skeleton string to a MacroProps. This method contains the primary parse loop.
|
||||
*
|
||||
* Internal: use the create() endpoint instead of this function.
|
||||
*/
|
||||
MacroProps parseSkeleton(const UnicodeString& skeletonString, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Given that the current segment represents an stem, parse it and save the result.
|
||||
*
|
||||
* @return The next state after parsing this stem, corresponding to what subset of options to expect.
|
||||
*/
|
||||
ParseState parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen,
|
||||
MacroProps& macros, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Given that the current segment represents an option, parse it and save the result.
|
||||
*
|
||||
* @return The next state after parsing this option, corresponding to what subset of options to
|
||||
* expect next.
|
||||
*/
|
||||
ParseState
|
||||
parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Main skeleton generator function. Appends the normalized skeleton for the MacroProps to the given
|
||||
* StringBuilder.
|
||||
*
|
||||
* Internal: use the create() endpoint instead of this function.
|
||||
*/
|
||||
void generateSkeleton(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
} // namespace skeleton
|
||||
|
||||
|
||||
/**
|
||||
* Namespace for utility methods that convert from StemEnum to corresponding objects or enums. This
|
||||
* applies to only the "Section 1" stems, those that are well-defined without an option.
|
||||
*/
|
||||
namespace stem_to_object {
|
||||
|
||||
Notation notation(skeleton::StemEnum stem);
|
||||
|
||||
MeasureUnit unit(skeleton::StemEnum stem);
|
||||
|
||||
Rounder rounder(skeleton::StemEnum stem);
|
||||
|
||||
UGroupingStrategy groupingStrategy(skeleton::StemEnum stem);
|
||||
|
||||
UNumberUnitWidth unitWidth(skeleton::StemEnum stem);
|
||||
|
||||
UNumberSignDisplay signDisplay(skeleton::StemEnum stem);
|
||||
|
||||
UNumberDecimalSeparatorDisplay decimalSeparatorDisplay(skeleton::StemEnum stem);
|
||||
|
||||
} // namespace stem_to_object
|
||||
|
||||
/**
|
||||
* Namespace for utility methods that convert from enums to stem strings. More complex object conversions
|
||||
* take place in the object_to_stem_string namespace.
|
||||
*/
|
||||
namespace enum_to_stem_string {
|
||||
|
||||
void groupingStrategy(UGroupingStrategy value, UnicodeString& sb);
|
||||
|
||||
void unitWidth(UNumberUnitWidth value, UnicodeString& sb);
|
||||
|
||||
void signDisplay(UNumberSignDisplay value, UnicodeString& sb);
|
||||
|
||||
void decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb);
|
||||
|
||||
} // namespace enum_to_stem_string
|
||||
|
||||
/**
|
||||
* Namespace for utility methods for processing stems and options that cannot be interpreted literally.
|
||||
*/
|
||||
namespace blueprint_helpers {
|
||||
|
||||
/** @return Whether we successfully found and parsed an exponent width option. */
|
||||
bool parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateExponentWidthOption(int32_t xyz, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
/** @return Whether we successfully found and parsed an exponent sign option. */
|
||||
bool parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void parseCurrencyOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
void parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateMeasureUnitOption(const MeasureUnit& measureUnit, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
void parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void parseFractionStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
void parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
/** @return Whether we successfully found and parsed a frac-sig option. */
|
||||
bool parseFracSigOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateIncrementOption(double increment, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
/** @return Whether we successfully found and parsed a rounding mode. */
|
||||
bool parseRoundingModeOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateRoundingModeOption(RoundingMode, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
void parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void generateNumberingSystemOption(NumberingSystem, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
} // namespace blueprint_helpers
|
||||
|
||||
/**
|
||||
* Namespace for utility methods for generating a token corresponding to each macro-prop. Each method
|
||||
* returns whether or not a token was written to the string builder.
|
||||
*/
|
||||
namespace generator_helpers {
|
||||
|
||||
bool notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool rounding(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool integerWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool symbols(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool sign(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
bool decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
} // namespace generator_helpers
|
||||
|
||||
/**
|
||||
* Struct for null-checking.
|
||||
* In Java, we can just check the object reference. In C++, we need a different method.
|
||||
*/
|
||||
struct SeenMacroProps {
|
||||
bool notation = false;
|
||||
bool unit = false;
|
||||
bool perUnit = false;
|
||||
bool rounder = false;
|
||||
bool grouper = false;
|
||||
bool padder = false;
|
||||
bool integerWidth = false;
|
||||
bool symbols = false;
|
||||
bool unitWidth = false;
|
||||
bool sign = false;
|
||||
bool decimal = false;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif //__SOURCE_NUMBER_SKELETONS_H__
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
|
@ -20,9 +20,9 @@ using namespace icu::numparse;
|
|||
using namespace icu::numparse::impl;
|
||||
|
||||
|
||||
StringSegment::StringSegment(const UnicodeString& str, parse_flags_t parseFlags)
|
||||
StringSegment::StringSegment(const UnicodeString& str, bool ignoreCase)
|
||||
: fStr(str), fStart(0), fEnd(str.length()),
|
||||
fFoldCase(0 != (parseFlags & PARSE_FLAG_IGNORE_CASE)) {}
|
||||
fFoldCase(ignoreCase) {}
|
||||
|
||||
int32_t StringSegment::getOffset() const {
|
||||
return fStart;
|
||||
|
|
|
@ -170,7 +170,7 @@ class ParsedNumber {
|
|||
*/
|
||||
class StringSegment : public UMemory, public ::icu::number::impl::CharSequence {
|
||||
public:
|
||||
explicit StringSegment(const UnicodeString& str, parse_flags_t parseFlags);
|
||||
StringSegment(const UnicodeString& str, bool ignoreCase);
|
||||
|
||||
int32_t getOffset() const;
|
||||
|
||||
|
@ -248,6 +248,8 @@ class StringSegment : public UMemory, public ::icu::number::impl::CharSequence {
|
|||
*/
|
||||
int32_t getCaseSensitivePrefixLength(const UnicodeString& other);
|
||||
|
||||
bool operator==(const UnicodeString& other) const;
|
||||
|
||||
private:
|
||||
const UnicodeString fStr;
|
||||
int32_t fStart;
|
||||
|
|
|
@ -26,6 +26,7 @@ as the functions are suppose to be called.
|
|||
It's usually best to have child dependencies called first. */
|
||||
typedef enum ECleanupI18NType {
|
||||
UCLN_I18N_START = -1,
|
||||
UCLN_I18N_NUMBER_SKELETONS,
|
||||
UCLN_I18N_NUMPARSE_UNISETS,
|
||||
UCLN_I18N_CURRENCY_SPACING,
|
||||
UCLN_I18N_SPOOF,
|
||||
|
|
|
@ -105,7 +105,7 @@ typedef enum UNumberUnitWidth {
|
|||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_UNIT_WIDTH_NARROW,
|
||||
UNUM_UNIT_WIDTH_NARROW,
|
||||
|
||||
/**
|
||||
* Print an abbreviated version of the unit name. Similar to NARROW, but use a slightly wider abbreviation or
|
||||
|
@ -121,7 +121,7 @@ typedef enum UNumberUnitWidth {
|
|||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_UNIT_WIDTH_SHORT,
|
||||
UNUM_UNIT_WIDTH_SHORT,
|
||||
|
||||
/**
|
||||
* Print the full name of the unit, without any abbreviations.
|
||||
|
@ -132,7 +132,7 @@ typedef enum UNumberUnitWidth {
|
|||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_UNIT_WIDTH_FULL_NAME,
|
||||
UNUM_UNIT_WIDTH_FULL_NAME,
|
||||
|
||||
/**
|
||||
* Use the three-digit ISO XXX code in place of the symbol for displaying currencies. The behavior of this
|
||||
|
@ -143,7 +143,7 @@ typedef enum UNumberUnitWidth {
|
|||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_UNIT_WIDTH_ISO_CODE,
|
||||
UNUM_UNIT_WIDTH_ISO_CODE,
|
||||
|
||||
/**
|
||||
* Format the number according to the specified unit, but do not display the unit. For currencies, apply
|
||||
|
@ -152,14 +152,14 @@ typedef enum UNumberUnitWidth {
|
|||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_UNIT_WIDTH_HIDDEN,
|
||||
UNUM_UNIT_WIDTH_HIDDEN,
|
||||
|
||||
/**
|
||||
* One more than the highest UNumberUnitWidth value.
|
||||
*
|
||||
* @internal ICU 60: The numeric value may change over time; see ICU ticket #12420.
|
||||
*/
|
||||
UNUM_UNIT_WIDTH_COUNT
|
||||
UNUM_UNIT_WIDTH_COUNT
|
||||
} UNumberUnitWidth;
|
||||
|
||||
/**
|
||||
|
@ -186,7 +186,8 @@ typedef enum UNumberUnitWidth {
|
|||
* Note: This enum specifies the strategy for grouping sizes. To set which character to use as the
|
||||
* grouping separator, use the "symbols" setter.
|
||||
*
|
||||
* @draft ICU 61
|
||||
* @draft ICU 61 -- TODO: This should be renamed to UNumberGroupingStrategy before promoting to stable,
|
||||
* for consistency with the other enums.
|
||||
*/
|
||||
typedef enum UGroupingStrategy {
|
||||
/**
|
||||
|
@ -249,7 +250,14 @@ typedef enum UGroupingStrategy {
|
|||
*
|
||||
* @draft ICU 61
|
||||
*/
|
||||
UNUM_GROUPING_THOUSANDS
|
||||
UNUM_GROUPING_THOUSANDS,
|
||||
|
||||
/**
|
||||
* One more than the highest UNumberSignDisplay value.
|
||||
*
|
||||
* @internal ICU 62: The numeric value may change over time; see ICU ticket #12420.
|
||||
*/
|
||||
UNUM_GROUPING_COUNT
|
||||
|
||||
} UGroupingStrategy;
|
||||
|
||||
|
@ -363,22 +371,22 @@ typedef enum UNumberDecimalSeparatorDisplay {
|
|||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_DECIMAL_SEPARATOR_AUTO,
|
||||
UNUM_DECIMAL_SEPARATOR_AUTO,
|
||||
|
||||
/**
|
||||
* Always show the decimal separator, even if there are no digits to display after the separator.
|
||||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
UNUM_DECIMAL_SEPARATOR_ALWAYS,
|
||||
UNUM_DECIMAL_SEPARATOR_ALWAYS,
|
||||
|
||||
/**
|
||||
* One more than the highest UNumberDecimalSeparatorDisplay value.
|
||||
*
|
||||
* @internal ICU 60: The numeric value may change over time; see ICU ticket #12420.
|
||||
*/
|
||||
UNUM_DECIMAL_SEPARATOR_COUNT
|
||||
} UNumberDecimalMarkDisplay;
|
||||
UNUM_DECIMAL_SEPARATOR_COUNT
|
||||
} UNumberDecimalSeparatorDisplay;
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
|
|
@ -107,6 +107,59 @@ class NumberSkeletonImpl {
|
|||
/** For mapping from ordinal back to StemEnum in Java. */
|
||||
static final StemEnum[] STEM_ENUM_VALUES = StemEnum.values();
|
||||
|
||||
/** A data structure for mapping from stem strings to the stem enum. Built at startup. */
|
||||
static final String SERIALIZED_STEM_TRIE = buildStemTrie();
|
||||
|
||||
static String buildStemTrie() {
|
||||
CharsTrieBuilder b = new CharsTrieBuilder();
|
||||
|
||||
// Section 1:
|
||||
b.add("compact-short", StemEnum.STEM_COMPACT_SHORT.ordinal());
|
||||
b.add("compact-long", StemEnum.STEM_COMPACT_LONG.ordinal());
|
||||
b.add("scientific", StemEnum.STEM_SCIENTIFIC.ordinal());
|
||||
b.add("engineering", StemEnum.STEM_ENGINEERING.ordinal());
|
||||
b.add("notation-simple", StemEnum.STEM_NOTATION_SIMPLE.ordinal());
|
||||
b.add("base-unit", StemEnum.STEM_BASE_UNIT.ordinal());
|
||||
b.add("percent", StemEnum.STEM_PERCENT.ordinal());
|
||||
b.add("permille", StemEnum.STEM_PERMILLE.ordinal());
|
||||
b.add("round-integer", StemEnum.STEM_ROUND_INTEGER.ordinal());
|
||||
b.add("round-unlimited", StemEnum.STEM_ROUND_UNLIMITED.ordinal());
|
||||
b.add("round-currency-standard", StemEnum.STEM_ROUND_CURRENCY_STANDARD.ordinal());
|
||||
b.add("round-currency-cash", StemEnum.STEM_ROUND_CURRENCY_CASH.ordinal());
|
||||
b.add("group-off", StemEnum.STEM_GROUP_OFF.ordinal());
|
||||
b.add("group-min2", StemEnum.STEM_GROUP_MIN2.ordinal());
|
||||
b.add("group-auto", StemEnum.STEM_GROUP_AUTO.ordinal());
|
||||
b.add("group-on-aligned", StemEnum.STEM_GROUP_ON_ALIGNED.ordinal());
|
||||
b.add("group-thousands", StemEnum.STEM_GROUP_THOUSANDS.ordinal());
|
||||
b.add("latin", StemEnum.STEM_LATIN.ordinal());
|
||||
b.add("unit-width-narrow", StemEnum.STEM_UNIT_WIDTH_NARROW.ordinal());
|
||||
b.add("unit-width-short", StemEnum.STEM_UNIT_WIDTH_SHORT.ordinal());
|
||||
b.add("unit-width-full-name", StemEnum.STEM_UNIT_WIDTH_FULL_NAME.ordinal());
|
||||
b.add("unit-width-iso-code", StemEnum.STEM_UNIT_WIDTH_ISO_CODE.ordinal());
|
||||
b.add("unit-width-hidden", StemEnum.STEM_UNIT_WIDTH_HIDDEN.ordinal());
|
||||
b.add("sign-auto", StemEnum.STEM_SIGN_AUTO.ordinal());
|
||||
b.add("sign-always", StemEnum.STEM_SIGN_ALWAYS.ordinal());
|
||||
b.add("sign-never", StemEnum.STEM_SIGN_NEVER.ordinal());
|
||||
b.add("sign-accounting", StemEnum.STEM_SIGN_ACCOUNTING.ordinal());
|
||||
b.add("sign-accounting-always", StemEnum.STEM_SIGN_ACCOUNTING_ALWAYS.ordinal());
|
||||
b.add("sign-except-zero", StemEnum.STEM_SIGN_EXCEPT_ZERO.ordinal());
|
||||
b.add("sign-accounting-except-zero", StemEnum.STEM_SIGN_ACCOUNTING_EXCEPT_ZERO.ordinal());
|
||||
b.add("decimal-auto", StemEnum.STEM_DECIMAL_AUTO.ordinal());
|
||||
b.add("decimal-always", StemEnum.STEM_DECIMAL_ALWAYS.ordinal());
|
||||
|
||||
// Section 2:
|
||||
b.add("round-increment", StemEnum.STEM_ROUND_INCREMENT.ordinal());
|
||||
b.add("measure-unit", StemEnum.STEM_MEASURE_UNIT.ordinal());
|
||||
b.add("per-measure-unit", StemEnum.STEM_PER_MEASURE_UNIT.ordinal());
|
||||
b.add("currency", StemEnum.STEM_CURRENCY.ordinal());
|
||||
b.add("integer-width", StemEnum.STEM_INTEGER_WIDTH.ordinal());
|
||||
b.add("numbering-system", StemEnum.STEM_NUMBERING_SYSTEM.ordinal());
|
||||
|
||||
// Build the CharsTrie
|
||||
// TODO: Use SLOW or FAST here?
|
||||
return b.buildCharSequence(StringTrieBuilder.Option.FAST).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for methods that convert from StemEnum to corresponding objects or enums. This
|
||||
* applies to only the "Section 1" stems, those that are well-defined without an option.
|
||||
|
@ -126,7 +179,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_NOTATION_SIMPLE:
|
||||
return Notation.simple();
|
||||
default:
|
||||
return null;
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +192,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_PERMILLE:
|
||||
return NoUnit.PERMILLE;
|
||||
default:
|
||||
return null;
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +207,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_ROUND_CURRENCY_CASH:
|
||||
return Rounder.currency(CurrencyUsage.CASH);
|
||||
default:
|
||||
return null;
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +224,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_GROUP_THOUSANDS:
|
||||
return GroupingStrategy.THOUSANDS;
|
||||
default:
|
||||
return null;
|
||||
return null; // for objects, throw; for enums, return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +241,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_UNIT_WIDTH_HIDDEN:
|
||||
return UnitWidth.HIDDEN;
|
||||
default:
|
||||
return null;
|
||||
return null; // for objects, throw; for enums, return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +262,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO:
|
||||
return SignDisplay.ACCOUNTING_EXCEPT_ZERO;
|
||||
default:
|
||||
return null;
|
||||
return null; // for objects, throw; for enums, return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +273,7 @@ class NumberSkeletonImpl {
|
|||
case STEM_DECIMAL_ALWAYS:
|
||||
return DecimalSeparatorDisplay.ALWAYS;
|
||||
default:
|
||||
return null;
|
||||
return null; // for objects, throw; for enums, return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -317,58 +370,6 @@ class NumberSkeletonImpl {
|
|||
}
|
||||
}
|
||||
|
||||
/** A data structure for mapping from stem strings to the stem enum. Built at startup. */
|
||||
static final String SERIALIZED_STEM_TRIE = buildStemTrie();
|
||||
|
||||
static String buildStemTrie() {
|
||||
CharsTrieBuilder b = new CharsTrieBuilder();
|
||||
|
||||
// Section 1:
|
||||
b.add("compact-short", StemEnum.STEM_COMPACT_SHORT.ordinal());
|
||||
b.add("compact-long", StemEnum.STEM_COMPACT_LONG.ordinal());
|
||||
b.add("scientific", StemEnum.STEM_SCIENTIFIC.ordinal());
|
||||
b.add("engineering", StemEnum.STEM_ENGINEERING.ordinal());
|
||||
b.add("notation-simple", StemEnum.STEM_NOTATION_SIMPLE.ordinal());
|
||||
b.add("base-unit", StemEnum.STEM_BASE_UNIT.ordinal());
|
||||
b.add("percent", StemEnum.STEM_PERCENT.ordinal());
|
||||
b.add("permille", StemEnum.STEM_PERMILLE.ordinal());
|
||||
b.add("round-integer", StemEnum.STEM_ROUND_INTEGER.ordinal());
|
||||
b.add("round-unlimited", StemEnum.STEM_ROUND_UNLIMITED.ordinal());
|
||||
b.add("round-currency-standard", StemEnum.STEM_ROUND_CURRENCY_STANDARD.ordinal());
|
||||
b.add("round-currency-cash", StemEnum.STEM_ROUND_CURRENCY_CASH.ordinal());
|
||||
b.add("group-off", StemEnum.STEM_GROUP_OFF.ordinal());
|
||||
b.add("group-min2", StemEnum.STEM_GROUP_MIN2.ordinal());
|
||||
b.add("group-auto", StemEnum.STEM_GROUP_AUTO.ordinal());
|
||||
b.add("group-on-aligned", StemEnum.STEM_GROUP_ON_ALIGNED.ordinal());
|
||||
b.add("group-thousands", StemEnum.STEM_GROUP_THOUSANDS.ordinal());
|
||||
b.add("latin", StemEnum.STEM_LATIN.ordinal());
|
||||
b.add("unit-width-narrow", StemEnum.STEM_UNIT_WIDTH_NARROW.ordinal());
|
||||
b.add("unit-width-short", StemEnum.STEM_UNIT_WIDTH_SHORT.ordinal());
|
||||
b.add("unit-width-full-name", StemEnum.STEM_UNIT_WIDTH_FULL_NAME.ordinal());
|
||||
b.add("unit-width-iso-code", StemEnum.STEM_UNIT_WIDTH_ISO_CODE.ordinal());
|
||||
b.add("unit-width-hidden", StemEnum.STEM_UNIT_WIDTH_HIDDEN.ordinal());
|
||||
b.add("sign-auto", StemEnum.STEM_SIGN_AUTO.ordinal());
|
||||
b.add("sign-always", StemEnum.STEM_SIGN_ALWAYS.ordinal());
|
||||
b.add("sign-never", StemEnum.STEM_SIGN_NEVER.ordinal());
|
||||
b.add("sign-accounting", StemEnum.STEM_SIGN_ACCOUNTING.ordinal());
|
||||
b.add("sign-accounting-always", StemEnum.STEM_SIGN_ACCOUNTING_ALWAYS.ordinal());
|
||||
b.add("sign-except-zero", StemEnum.STEM_SIGN_EXCEPT_ZERO.ordinal());
|
||||
b.add("sign-accounting-except-zero", StemEnum.STEM_SIGN_ACCOUNTING_EXCEPT_ZERO.ordinal());
|
||||
b.add("decimal-auto", StemEnum.STEM_DECIMAL_AUTO.ordinal());
|
||||
b.add("decimal-always", StemEnum.STEM_DECIMAL_ALWAYS.ordinal());
|
||||
|
||||
// Section 2:
|
||||
b.add("round-increment", StemEnum.STEM_ROUND_INCREMENT.ordinal());
|
||||
b.add("measure-unit", StemEnum.STEM_MEASURE_UNIT.ordinal());
|
||||
b.add("per-measure-unit", StemEnum.STEM_PER_MEASURE_UNIT.ordinal());
|
||||
b.add("currency", StemEnum.STEM_CURRENCY.ordinal());
|
||||
b.add("integer-width", StemEnum.STEM_INTEGER_WIDTH.ordinal());
|
||||
b.add("numbering-system", StemEnum.STEM_NUMBERING_SYSTEM.ordinal());
|
||||
|
||||
// TODO: Use SLOW or FAST here?
|
||||
return b.buildCharSequence(StringTrieBuilder.Option.FAST).toString();
|
||||
}
|
||||
|
||||
/** Kebab case versions of the rounding mode enum. */
|
||||
static final String[] ROUNDING_MODE_STRINGS = {
|
||||
"up",
|
||||
|
@ -443,6 +444,7 @@ class NumberSkeletonImpl {
|
|||
CharsTrie stemTrie = new CharsTrie(SERIALIZED_STEM_TRIE, 0);
|
||||
ParseState stem = ParseState.STATE_NULL;
|
||||
int offset = 0;
|
||||
|
||||
// Primary skeleton parse loop:
|
||||
while (offset < segment.length()) {
|
||||
int cp = segment.codePointAt(offset);
|
||||
|
@ -722,6 +724,10 @@ class NumberSkeletonImpl {
|
|||
|
||||
///// MAIN SKELETON GENERATION FUNCTION /////
|
||||
|
||||
/**
|
||||
* Main skeleton generator function. Appends the normalized skeleton for the MacroProps to the given
|
||||
* StringBuilder.
|
||||
*/
|
||||
private static void generateSkeleton(MacroProps macros, StringBuilder sb) {
|
||||
// Supported options
|
||||
if (macros.notation != null && GeneratorHelpers.notation(macros, sb)) {
|
||||
|
@ -779,9 +785,14 @@ class NumberSkeletonImpl {
|
|||
}
|
||||
}
|
||||
|
||||
///// BLUEPRINT HELPER FUNCTIONS (stem and options that cannot be interpreted literally) /////
|
||||
///// BLUEPRINT HELPER FUNCTIONS /////
|
||||
|
||||
/**
|
||||
* Utility class for methods for processing stems and options that cannot be interpreted literally.
|
||||
*/
|
||||
static final class BlueprintHelpers {
|
||||
|
||||
/** @return Whether we successfully found and parsed an exponent width option. */
|
||||
private static boolean parseExponentWidthOption(StringSegment segment, MacroProps macros) {
|
||||
if (segment.charAt(0) != '+') {
|
||||
return false;
|
||||
|
@ -808,6 +819,7 @@ class NumberSkeletonImpl {
|
|||
appendMultiple(sb, 'e', minExponentDigits);
|
||||
}
|
||||
|
||||
/** @return Whether we successfully found and parsed an exponent sign option. */
|
||||
private static boolean parseExponentSignOption(StringSegment segment, MacroProps macros) {
|
||||
// Get the sign display type out of the CharsTrie data structure.
|
||||
// TODO: Make this more efficient (avoid object allocation)? It shouldn't be very hot code.
|
||||
|
@ -977,6 +989,7 @@ class NumberSkeletonImpl {
|
|||
}
|
||||
}
|
||||
|
||||
/** @return Whether we successfully found and parsed a frac-sig option. */
|
||||
private static boolean parseFracSigOption(StringSegment segment, MacroProps macros) {
|
||||
if (segment.charAt(0) != '@') {
|
||||
return false;
|
||||
|
@ -1047,6 +1060,7 @@ class NumberSkeletonImpl {
|
|||
sb.append(increment.toPlainString());
|
||||
}
|
||||
|
||||
/** @return Whether we successfully found and parsed a rounding mode. */
|
||||
private static boolean parseRoundingModeOption(StringSegment segment, MacroProps macros) {
|
||||
for (int rm = 0; rm < ROUNDING_MODE_STRINGS.length; rm++) {
|
||||
if (segment.equals(ROUNDING_MODE_STRINGS[rm])) {
|
||||
|
@ -1127,6 +1141,10 @@ class NumberSkeletonImpl {
|
|||
|
||||
///// STEM GENERATION HELPER FUNCTIONS /////
|
||||
|
||||
/**
|
||||
* Utility class for methods for generating a token corresponding to each macro-prop. Each method
|
||||
* returns whether or not a token was written to the string builder.
|
||||
*/
|
||||
static final class GeneratorHelpers {
|
||||
|
||||
private static boolean notation(MacroProps macros, StringBuilder sb) {
|
||||
|
|
Loading…
Add table
Reference in a new issue