// Allow implicit conversion from char16_t* to UnicodeString for this file: // Helpful in toString methods and elsewhere. #define UNISTR_FROM_STRING_EXPLICIT
// 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);
}
inlinevoid appendMultiple(UnicodeString& sb, UChar32 cp, int32_t count) { for (int i = 0; i < count; i++) {
sb.append(cp);
}
}
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:
UPRV_UNREACHABLE_EXIT;
}
}
MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) { switch (stem) { case STEM_BASE_UNIT: return {}; case STEM_PERCENT: return MeasureUnit::getPercent(); case STEM_PERMILLE: return MeasureUnit::getPermille(); default:
UPRV_UNREACHABLE_EXIT;
}
}
Precision stem_to_object::precision(skeleton::StemEnum stem) { switch (stem) { case STEM_PRECISION_INTEGER: return Precision::integer(); case STEM_PRECISION_UNLIMITED: return Precision::unlimited(); case STEM_PRECISION_CURRENCY_STANDARD: return Precision::currency(UCURR_USAGE_STANDARD); case STEM_PRECISION_CURRENCY_CASH: return Precision::currency(UCURR_USAGE_CASH); default:
UPRV_UNREACHABLE_EXIT;
}
}
UNumberFormatRoundingMode stem_to_object::roundingMode(skeleton::StemEnum stem) { switch (stem) { case STEM_ROUNDING_MODE_CEILING: return UNUM_ROUND_CEILING; case STEM_ROUNDING_MODE_FLOOR: return UNUM_ROUND_FLOOR; case STEM_ROUNDING_MODE_DOWN: return UNUM_ROUND_DOWN; case STEM_ROUNDING_MODE_UP: return UNUM_ROUND_UP; case STEM_ROUNDING_MODE_HALF_EVEN: return UNUM_ROUND_HALFEVEN; case STEM_ROUNDING_MODE_HALF_ODD: return UNUM_ROUND_HALF_ODD; case STEM_ROUNDING_MODE_HALF_CEILING: return UNUM_ROUND_HALF_CEILING; case STEM_ROUNDING_MODE_HALF_FLOOR: return UNUM_ROUND_HALF_FLOOR; case STEM_ROUNDING_MODE_HALF_DOWN: return UNUM_ROUND_HALFDOWN; case STEM_ROUNDING_MODE_HALF_UP: return UNUM_ROUND_HALFUP; case STEM_ROUNDING_MODE_UNNECESSARY: return UNUM_ROUND_UNNECESSARY; default:
UPRV_UNREACHABLE_EXIT;
}
}
UNumberGroupingStrategy 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_FORMAL: return UNUM_UNIT_WIDTH_FORMAL; case STEM_UNIT_WIDTH_VARIANT: return UNUM_UNIT_WIDTH_VARIANT; 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; case STEM_SIGN_NEGATIVE: return UNUM_SIGN_NEGATIVE; case STEM_SIGN_ACCOUNTING_NEGATIVE: return UNUM_SIGN_ACCOUNTING_NEGATIVE; 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::roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb) { switch (value) { case UNUM_ROUND_CEILING:
sb.append(u"rounding-mode-ceiling", -1); break; case UNUM_ROUND_FLOOR:
sb.append(u"rounding-mode-floor", -1); break; case UNUM_ROUND_DOWN:
sb.append(u"rounding-mode-down", -1); break; case UNUM_ROUND_UP:
sb.append(u"rounding-mode-up", -1); break; case UNUM_ROUND_HALFEVEN:
sb.append(u"rounding-mode-half-even", -1); break; case UNUM_ROUND_HALF_ODD:
sb.append(u"rounding-mode-half-odd", -1); break; case UNUM_ROUND_HALF_CEILING:
sb.append(u"rounding-mode-half-ceiling", -1); break; case UNUM_ROUND_HALF_FLOOR:
sb.append(u"rounding-mode-half-floor", -1); break; case UNUM_ROUND_HALFDOWN:
sb.append(u"rounding-mode-half-down", -1); break; case UNUM_ROUND_HALFUP:
sb.append(u"rounding-mode-half-up", -1); break; case UNUM_ROUND_UNNECESSARY:
sb.append(u"rounding-mode-unnecessary", -1); break; default:
UPRV_UNREACHABLE_EXIT;
}
}
void enum_to_stem_string::groupingStrategy(UNumberGroupingStrategy 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:
UPRV_UNREACHABLE_EXIT;
}
}
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_FORMAL:
sb.append(u"unit-width-formal", -1); break; case UNUM_UNIT_WIDTH_VARIANT:
sb.append(u"unit-width-variant", -1); break; case UNUM_UNIT_WIDTH_HIDDEN:
sb.append(u"unit-width-hidden", -1); break; default:
UPRV_UNREACHABLE_EXIT;
}
}
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; case UNUM_SIGN_NEGATIVE:
sb.append(u"sign-negative", -1); break; case UNUM_SIGN_ACCOUNTING_NEGATIVE:
sb.append(u"sign-accounting-negative", -1); break; default:
UPRV_UNREACHABLE_EXIT;
}
}
// Add a trailing whitespace to the end of the skeleton string to make code cleaner.
UnicodeString tempSkeletonString(skeletonString);
tempSkeletonString.append(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(); if (U_FAILURE(status)) {
errOffset = segment.getOffset(); return macros;
}
// Consume the segment:
segment.adjustOffset(offset);
offset = 0;
} elseif (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;
errOffset = segment.getOffset(); 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;
errOffset = segment.getOffset(); return macros;
}
// Does the current stem require an option? if (isTokenSeparator && stem != STATE_NULL) { switch (stem) { case STATE_INCREMENT_PRECISION: case STATE_MEASURE_UNIT: case STATE_PER_MEASURE_UNIT: case STATE_IDENTIFIER_UNIT: case STATE_UNIT_USAGE: case STATE_CURRENCY_UNIT: case STATE_INTEGER_WIDTH: case STATE_NUMBERING_SYSTEM: case STATE_SCALE: // segment.setLength(U16_LENGTH(cp)); // for error message // throw new SkeletonSyntaxException("Stem requires an option", segment);
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
errOffset = segment.getOffset(); return macros; default: break;
}
stem = STATE_NULL;
}
// First check for "blueprint" stems, which start with a "signal char" switch (segment.charAt(0)) { case u'.':
CHECK_NULL(seen, precision, status);
blueprint_helpers::parseFractionStem(segment, macros, status); return STATE_FRACTION_PRECISION; case u'@':
CHECK_NULL(seen, precision, status);
blueprint_helpers::parseDigitsStem(segment, macros, status); return STATE_PRECISION; case u'E':
CHECK_NULL(seen, notation, status);
blueprint_helpers::parseScientificStem(segment, macros, status); return STATE_NULL; case u'0':
CHECK_NULL(seen, integerWidth, status);
blueprint_helpers::parseIntegerStem(segment, macros, status); return STATE_NULL; default: break;
}
// 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_PERCENT_100:
CHECK_NULL(seen, scale, status);
CHECK_NULL(seen, unit, status);
macros.scale = Scale::powerOfTen(2);
macros.unit = NoUnit::percent(); return STATE_NULL;
case STEM_PRECISION_INTEGER: case STEM_PRECISION_UNLIMITED: case STEM_PRECISION_CURRENCY_STANDARD: case STEM_PRECISION_CURRENCY_CASH:
CHECK_NULL(seen, precision, status);
macros.precision = stem_to_object::precision(stem); switch (stem) { case STEM_PRECISION_INTEGER: return STATE_FRACTION_PRECISION; // allows for "precision-integer/@##" default: return STATE_PRECISION;
}
case STEM_ROUNDING_MODE_CEILING: case STEM_ROUNDING_MODE_FLOOR: case STEM_ROUNDING_MODE_DOWN: case STEM_ROUNDING_MODE_UP: case STEM_ROUNDING_MODE_HALF_EVEN: case STEM_ROUNDING_MODE_HALF_ODD: case STEM_ROUNDING_MODE_HALF_CEILING: case STEM_ROUNDING_MODE_HALF_FLOOR: case STEM_ROUNDING_MODE_HALF_DOWN: case STEM_ROUNDING_MODE_HALF_UP: case STEM_ROUNDING_MODE_UNNECESSARY:
CHECK_NULL(seen, roundingMode, status);
macros.roundingMode = stem_to_object::roundingMode(stem); return STATE_NULL;
case STEM_INTEGER_WIDTH_TRUNC:
CHECK_NULL(seen, integerWidth, status);
macros.integerWidth = IntegerWidth::zeroFillTo(0).truncateAt(0); return STATE_NULL;
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_FORMAL: case STEM_UNIT_WIDTH_VARIANT: 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: case STEM_SIGN_NEGATIVE: case STEM_SIGN_ACCOUNTING_NEGATIVE:
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_PRECISION_INCREMENT:
CHECK_NULL(seen, precision, status); return STATE_INCREMENT_PRECISION;
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_UNIT:
CHECK_NULL(seen, unit, status);
CHECK_NULL(seen, perUnit, status); return STATE_IDENTIFIER_UNIT;
case STEM_UNIT_USAGE:
CHECK_NULL(seen, usage, status); return STATE_UNIT_USAGE;
case STEM_CURRENCY:
CHECK_NULL(seen, unit, status);
CHECK_NULL(seen, perUnit, 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;
case STEM_SCALE:
CHECK_NULL(seen, scale, status); return STATE_SCALE;
// Supported options if (GeneratorHelpers::notation(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::unit(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::usage(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::precision(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::roundingMode(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::grouping(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::integerWidth(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::symbols(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::unitWidth(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::sign(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::decimal(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; } if (GeneratorHelpers::scale(macros, sb, status)) {
sb.append(u' ');
} if (U_FAILURE(status)) { return; }
// Unsupported options if (!macros.padder.isBogus()) {
status = U_UNSUPPORTED_ERROR; return;
} if (macros.unitDisplayCase.isSet()) {
status = U_UNSUPPORTED_ERROR; return;
} if (macros.affixProvider != nullptr) {
status = U_UNSUPPORTED_ERROR; return;
} if (macros.rules != nullptr) {
status = U_UNSUPPORTED_ERROR; return;
}
// Remove the trailing space if (sb.length() > 0) {
sb.truncate(sb.length() - 1);
}
}
bool blueprint_helpers::parseExponentWidthOption(const StringSegment& segment, MacroProps& macros,
UErrorCode&) { if (!isWildcardChar(segment.charAt(0))) { returnfalse;
}
int32_t offset = 1;
int32_t minExp = 0; for (; offset < segment.length(); offset++) { if (segment.charAt(offset) == u'e') {
minExp++;
} else { break;
}
} if (offset < segment.length()) { returnfalse;
} // Use the public APIs to enforce bounds checking
macros.notation = static_cast<ScientificNotation&>(macros.notation).withMinExponentDigits(minExp); returntrue;
}
bool
blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { // Get the sign display type out of the CharsTrie data structure.
UCharsTrie tempStemTrie(kSerializedStemTrie);
UStringTrieResult result = tempStemTrie.next(
segment.toTempUnicodeString().getBuffer(),
segment.length()); if (result != USTRINGTRIE_INTERMEDIATE_VALUE && result != USTRINGTRIE_FINAL_VALUE) { returnfalse;
} auto sign = stem_to_object::signDisplay(static_cast<StemEnum>(tempStemTrie.getValue())); if (sign == UNUM_SIGN_COUNT) { returnfalse;
}
macros.notation = static_cast<ScientificNotation&>(macros.notation).withExponentSignDisplay(sign); returntrue;
}
void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) { // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us if (segment.length() != 3) {
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
} const char16_t* currencyCode = segment.toTempUnicodeString().getBuffer();
UErrorCode localStatus = U_ZERO_ERROR;
CurrencyUnit currency(currencyCode, localStatus); if (U_FAILURE(localStatus)) { // Not 3 ascii chars // throw new SkeletonSyntaxException("Invalid currency", segment);
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
} // Slicing is OK
macros.unit = currency; // NOLINT
}
// NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric) // http://unicode.org/reports/tr35/#Validity_Data int firstHyphen = 0; while (firstHyphen < stemString.length() && stemString.charAt(firstHyphen) != '-') {
firstHyphen++;
} if (firstHyphen == stemString.length()) { // throw new SkeletonSyntaxException("Invalid measure unit option", segment);
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
}
// Need to do char <-> char16_t conversion...
CharString type;
SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
CharString subType;
SKELETON_UCHAR_TO_CHAR(subType, stemString, firstHyphen + 1, stemString.length(), status);
// Note: the largest type as of this writing (Aug 2020) is "volume", which has 33 units. static constexpr int32_t CAPACITY = 40;
MeasureUnit units[CAPACITY];
UErrorCode localStatus = U_ZERO_ERROR;
int32_t numUnits = MeasureUnit::getAvailable(type.data(), units, CAPACITY, localStatus); if (U_FAILURE(localStatus)) { // More than 30 units in this type?
status = U_INTERNAL_PROGRAM_ERROR; return;
} for (int32_t i = 0; i < numUnits; i++) { auto& unit = units[i]; if (uprv_strcmp(subType.data(), unit.getSubtype()) == 0) {
macros.unit = unit; return;
}
}
// throw new SkeletonSyntaxException("Unknown measure unit", segment);
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
}
void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) { // A little bit of a hack: save the current unit (numerator), call the main measure unit // parsing code, put back the numerator unit, and put the new unit into per-unit.
MeasureUnit numerator = macros.unit;
parseMeasureUnitOption(segment, macros, status); if (U_FAILURE(status)) { return; }
macros.perUnit = macros.unit;
macros.unit = numerator;
}
void blueprint_helpers::parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) { // Need to do char <-> char16_t conversion...
U_ASSERT(U_SUCCESS(status));
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
ErrorCode internalStatus;
macros.unit = MeasureUnit::forIdentifier(buffer.toStringPiece(), internalStatus); if (internalStatus.isFailure()) { // throw new SkeletonSyntaxException("Invalid core unit identifier", segment, e);
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
}
}
void blueprint_helpers::parseUnitUsageOption(const StringSegment &segment, MacroProps ¯os,
UErrorCode &status) { // Need to do char <-> char16_t conversion...
U_ASSERT(U_SUCCESS(status));
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
macros.usage.set(buffer.toStringPiece()); // We do not do any validation of the usage string: it depends on the // unitPreferenceData in the units resources.
}
void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
U_ASSERT(segment.charAt(0) == u'.');
int32_t offset = 1;
int32_t minFrac = 0;
int32_t maxFrac; for (; offset < segment.length(); offset++) { if (segment.charAt(offset) == u'0') {
minFrac++;
} else { break;
}
} if (offset < segment.length()) { if (isWildcardChar(segment.charAt(offset))) {
maxFrac = -1;
offset++;
} else {
maxFrac = minFrac; for (; offset < segment.length(); offset++) { if (segment.charAt(offset) == u'#') {
maxFrac++;
} else { break;
}
}
}
} else {
maxFrac = minFrac;
} if (offset < segment.length()) { // throw new SkeletonSyntaxException("Invalid fraction stem", segment);
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
} // Use the public APIs to enforce bounds checking if (maxFrac == -1) { if (minFrac == 0) {
macros.precision = Precision::unlimited();
} else {
macros.precision = Precision::minFraction(minFrac);
}
} else {
macros.precision = Precision::minMaxFraction(minFrac, maxFrac);
}
}
void blueprint_helpers::parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) { // Need to do char <-> char16_t conversion...
U_ASSERT(U_SUCCESS(status));
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
NumberingSystem* ns = NumberingSystem::createInstanceByName(buffer.data(), status); if (ns == nullptr || U_FAILURE(status)) { // This is a skeleton syntax error; don't bubble up the low-level NumberingSystem error // throw new SkeletonSyntaxException("Unknown numbering system", segment);
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
}
macros.symbols.setTo(ns);
}
void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb,
UErrorCode&) { // Need to do char <-> char16_t conversion...
sb.append(UnicodeString(ns.getName(), -1, US_INV));
}
void blueprint_helpers::parseScaleOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) { // Need to do char <-> char16_t conversion...
U_ASSERT(U_SUCCESS(status));
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
LocalPointer<DecNum> decnum(new DecNum(), status); if (U_FAILURE(status)) { return; }
decnum->setTo({buffer.data(), buffer.length()}, status); if (U_FAILURE(status) || decnum->isSpecial()) { // This is a skeleton syntax error; don't let the low-level decnum error bubble up
status = U_NUMBER_SKELETON_SYNTAX_ERROR; return;
}
// NOTE: The constructor will optimize the decnum for us if possible.
macros.scale = {0, decnum.orphan()};
}
void blueprint_helpers::generateScaleOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb,
UErrorCode& status) { // Utilize DecimalQuantity/double_conversion to format this for us.
DecimalQuantity dq; if (arbitrary != nullptr) {
dq.setToDecNum(*arbitrary, status); if (U_FAILURE(status)) { return; }
} else {
dq.setToInt(1);
}
dq.adjustMagnitude(magnitude);
dq.roundToInfinity();
sb.append(dq.toPlainString());
}
bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { if (macros.notation.fType == Notation::NTN_COMPACT) {
UNumberCompactStyle style = macros.notation.fUnion.compactStyle; if (style == UNumberCompactStyle::UNUM_LONG) {
sb.append(u"compact-long", -1); returntrue;
} elseif (style == UNumberCompactStyle::UNUM_SHORT) {
sb.append(u"compact-short", -1); returntrue;
} else { // Compact notation generated from custom data (not supported in skeleton) // The other compact notations are literals
status = U_UNSUPPORTED_ERROR; returnfalse;
}
} elseif (macros.notation.fType == Notation::NTN_SCIENTIFIC) { const Notation::ScientificSettings& impl = macros.notation.fUnion.scientific; if (impl.fEngineeringInterval == 3) {
sb.append(u"engineering", -1);
} else {
sb.append(u"scientific", -1);
} if (impl.fMinExponentDigits > 1) {
sb.append(u'/');
blueprint_helpers::generateExponentWidthOption(impl.fMinExponentDigits, sb, status); if (U_FAILURE(status)) { returnfalse;
}
} if (impl.fExponentSignDisplay != UNUM_SIGN_AUTO) {
sb.append(u'/');
enum_to_stem_string::signDisplay(impl.fExponentSignDisplay, sb);
} returntrue;
} else { // Default value is not shown in normalized form returnfalse;
}
}
bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
MeasureUnit unit = macros.unit; if (!utils::unitIsBaseUnit(macros.perUnit)) { if (utils::unitIsCurrency(macros.unit) || utils::unitIsCurrency(macros.perUnit)) {
status = U_UNSUPPORTED_ERROR; returnfalse;
}
unit = unit.product(macros.perUnit.reciprocal(status), status);
}
if (utils::unitIsCurrency(unit)) {
sb.append(u"currency/", -1);
CurrencyUnit currency(unit, status); if (U_FAILURE(status)) { returnfalse;
}
blueprint_helpers::generateCurrencyOption(currency, sb, status); returntrue;
} elseif (utils::unitIsBaseUnit(unit)) { // Default value is not shown in normalized form returnfalse;
} elseif (utils::unitIsPercent(unit)) {
sb.append(u"percent", -1); returntrue;
} elseif (utils::unitIsPermille(unit)) {
sb.append(u"permille", -1); returntrue;
} else {
sb.append(u"unit/", -1);
sb.append(unit.getIdentifier()); returntrue;
}
}
// Definitions of public API methods (put here for dependency disentanglement)
#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) // Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method // is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation // inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is // fully defined. However, since each translation unit explicitly instantiates all the necessary template classes, // they will all be passed to the linker, and the linker will still find and export all the class members. #pragma warning(push) #pragma warning(disable: 4661) #endif
// Declare all classes that implement NumberFormatterSettings // See https://stackoverflow.com/a/495056/1407170 template class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>; template class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.