diff options
Diffstat (limited to 'deps/node/deps/icu-small/source/i18n/number_patternstring.cpp')
-rw-r--r-- | deps/node/deps/icu-small/source/i18n/number_patternstring.cpp | 1070 |
1 files changed, 0 insertions, 1070 deletions
diff --git a/deps/node/deps/icu-small/source/i18n/number_patternstring.cpp b/deps/node/deps/icu-small/source/i18n/number_patternstring.cpp deleted file mode 100644 index 63195eed..00000000 --- a/deps/node/deps/icu-small/source/i18n/number_patternstring.cpp +++ /dev/null @@ -1,1070 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -// Allow implicit conversion from char16_t* to UnicodeString for this file: -// Helpful in toString methods and elsewhere. -#define UNISTR_FROM_STRING_EXPLICIT -#define UNISTR_FROM_CHAR_EXPLICIT - -#include "uassert.h" -#include "number_patternstring.h" -#include "unicode/utf16.h" -#include "number_utils.h" -#include "number_roundingutils.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, - UErrorCode& status) { - patternInfo.consumePattern(patternString, status); -} - -DecimalFormatProperties -PatternParser::parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding, - UErrorCode& status) { - DecimalFormatProperties properties; - parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status); - return properties; -} - -DecimalFormatProperties PatternParser::parseToProperties(const UnicodeString& pattern, - UErrorCode& status) { - return parseToProperties(pattern, IGNORE_ROUNDING_NEVER, status); -} - -void -PatternParser::parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties, - IgnoreRounding ignoreRounding, UErrorCode& status) { - parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status); -} - - -char16_t ParsedPatternInfo::charAt(int32_t flags, int32_t index) const { - const Endpoints& endpoints = getEndpoints(flags); - if (index < 0 || index >= endpoints.end - endpoints.start) { - U_ASSERT(false); - } - return pattern.charAt(endpoints.start + index); -} - -int32_t ParsedPatternInfo::length(int32_t flags) const { - return getLengthFromEndpoints(getEndpoints(flags)); -} - -int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints& endpoints) { - return endpoints.end - endpoints.start; -} - -UnicodeString ParsedPatternInfo::getString(int32_t flags) const { - const Endpoints& endpoints = getEndpoints(flags); - if (endpoints.start == endpoints.end) { - return UnicodeString(); - } - // Create a new UnicodeString - return UnicodeString(pattern, endpoints.start, endpoints.end - endpoints.start); -} - -const Endpoints& ParsedPatternInfo::getEndpoints(int32_t flags) const { - bool prefix = (flags & AFFIX_PREFIX) != 0; - bool isNegative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0; - bool padding = (flags & AFFIX_PADDING) != 0; - if (isNegative && padding) { - return negative.paddingEndpoints; - } else if (padding) { - return positive.paddingEndpoints; - } else if (prefix && isNegative) { - return negative.prefixEndpoints; - } else if (prefix) { - return positive.prefixEndpoints; - } else if (isNegative) { - return negative.suffixEndpoints; - } else { - return positive.suffixEndpoints; - } -} - -bool ParsedPatternInfo::positiveHasPlusSign() const { - return positive.hasPlusSign; -} - -bool ParsedPatternInfo::hasNegativeSubpattern() const { - return fHasNegativeSubpattern; -} - -bool ParsedPatternInfo::negativeHasMinusSign() const { - return negative.hasMinusSign; -} - -bool ParsedPatternInfo::hasCurrencySign() const { - return positive.hasCurrencySign || (fHasNegativeSubpattern && negative.hasCurrencySign); -} - -bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode& status) const { - return AffixUtils::containsType(pattern, type, status); -} - -bool ParsedPatternInfo::hasBody() const { - return positive.integerTotal > 0; -} - -///////////////////////////////////////////////////// -/// BEGIN RECURSIVE DESCENT PARSER IMPLEMENTATION /// -///////////////////////////////////////////////////// - -UChar32 ParsedPatternInfo::ParserState::peek() { - if (offset == pattern.length()) { - return -1; - } else { - return pattern.char32At(offset); - } -} - -UChar32 ParsedPatternInfo::ParserState::next() { - int codePoint = peek(); - offset += U16_LENGTH(codePoint); - return codePoint; -} - -void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - this->pattern = patternString; - - // This class is not intended for writing twice! - // Use move assignment to overwrite instead. - U_ASSERT(state.offset == 0); - - // pattern := subpattern (';' subpattern)? - currentSubpattern = &positive; - consumeSubpattern(status); - if (U_FAILURE(status)) { return; } - if (state.peek() == u';') { - state.next(); // consume the ';' - // Don't consume the negative subpattern if it is empty (trailing ';') - if (state.peek() != -1) { - fHasNegativeSubpattern = true; - currentSubpattern = &negative; - consumeSubpattern(status); - if (U_FAILURE(status)) { return; } - } - } - if (state.peek() != -1) { - state.toParseException(u"Found unquoted special character"); - status = U_UNQUOTED_SPECIAL; - } -} - -void ParsedPatternInfo::consumeSubpattern(UErrorCode& status) { - // subpattern := literals? number exponent? literals? - consumePadding(PadPosition::UNUM_PAD_BEFORE_PREFIX, status); - if (U_FAILURE(status)) { return; } - consumeAffix(currentSubpattern->prefixEndpoints, status); - if (U_FAILURE(status)) { return; } - consumePadding(PadPosition::UNUM_PAD_AFTER_PREFIX, status); - if (U_FAILURE(status)) { return; } - consumeFormat(status); - if (U_FAILURE(status)) { return; } - consumeExponent(status); - if (U_FAILURE(status)) { return; } - consumePadding(PadPosition::UNUM_PAD_BEFORE_SUFFIX, status); - if (U_FAILURE(status)) { return; } - consumeAffix(currentSubpattern->suffixEndpoints, status); - if (U_FAILURE(status)) { return; } - consumePadding(PadPosition::UNUM_PAD_AFTER_SUFFIX, status); - if (U_FAILURE(status)) { return; } -} - -void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode& status) { - if (state.peek() != u'*') { - return; - } - if (currentSubpattern->hasPadding) { - state.toParseException(u"Cannot have multiple pad specifiers"); - status = U_MULTIPLE_PAD_SPECIFIERS; - return; - } - currentSubpattern->paddingLocation = paddingLocation; - currentSubpattern->hasPadding = true; - state.next(); // consume the '*' - currentSubpattern->paddingEndpoints.start = state.offset; - consumeLiteral(status); - currentSubpattern->paddingEndpoints.end = state.offset; -} - -void ParsedPatternInfo::consumeAffix(Endpoints& endpoints, UErrorCode& status) { - // literals := { literal } - endpoints.start = state.offset; - while (true) { - switch (state.peek()) { - case u'#': - case u'@': - case u';': - case u'*': - case u'.': - case u',': - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - case -1: - // Characters that cannot appear unquoted in a literal - // break outer; - goto after_outer; - - case u'%': - currentSubpattern->hasPercentSign = true; - break; - - case u'‰': - currentSubpattern->hasPerMilleSign = true; - break; - - case u'¤': - currentSubpattern->hasCurrencySign = true; - break; - - case u'-': - currentSubpattern->hasMinusSign = true; - break; - - case u'+': - currentSubpattern->hasPlusSign = true; - break; - - default: - break; - } - consumeLiteral(status); - if (U_FAILURE(status)) { return; } - } - after_outer: - endpoints.end = state.offset; -} - -void ParsedPatternInfo::consumeLiteral(UErrorCode& status) { - if (state.peek() == -1) { - state.toParseException(u"Expected unquoted literal but found EOL"); - status = U_PATTERN_SYNTAX_ERROR; - return; - } else if (state.peek() == u'\'') { - state.next(); // consume the starting quote - while (state.peek() != u'\'') { - if (state.peek() == -1) { - state.toParseException(u"Expected quoted literal but found EOL"); - status = U_PATTERN_SYNTAX_ERROR; - return; - } else { - state.next(); // consume a quoted character - } - } - state.next(); // consume the ending quote - } else { - // consume a non-quoted literal character - state.next(); - } -} - -void ParsedPatternInfo::consumeFormat(UErrorCode& status) { - consumeIntegerFormat(status); - if (U_FAILURE(status)) { return; } - if (state.peek() == u'.') { - state.next(); // consume the decimal point - currentSubpattern->hasDecimal = true; - currentSubpattern->widthExceptAffixes += 1; - consumeFractionFormat(status); - if (U_FAILURE(status)) { return; } - } -} - -void ParsedPatternInfo::consumeIntegerFormat(UErrorCode& status) { - // Convenience reference: - ParsedSubpatternInfo& result = *currentSubpattern; - - while (true) { - switch (state.peek()) { - case u',': - result.widthExceptAffixes += 1; - result.groupingSizes <<= 16; - break; - - case u'#': - if (result.integerNumerals > 0) { - state.toParseException(u"# cannot follow 0 before decimal point"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.groupingSizes += 1; - if (result.integerAtSigns > 0) { - result.integerTrailingHashSigns += 1; - } else { - result.integerLeadingHashSigns += 1; - } - result.integerTotal += 1; - break; - - case u'@': - if (result.integerNumerals > 0) { - state.toParseException(u"Cannot mix 0 and @"); - status = U_UNEXPECTED_TOKEN; - return; - } - if (result.integerTrailingHashSigns > 0) { - state.toParseException(u"Cannot nest # inside of a run of @"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.groupingSizes += 1; - result.integerAtSigns += 1; - result.integerTotal += 1; - break; - - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - if (result.integerAtSigns > 0) { - state.toParseException(u"Cannot mix @ and 0"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.groupingSizes += 1; - result.integerNumerals += 1; - result.integerTotal += 1; - if (!result.rounding.isZero() || state.peek() != u'0') { - result.rounding.appendDigit(static_cast<int8_t>(state.peek() - u'0'), 0, true); - } - break; - - default: - goto after_outer; - } - state.next(); // consume the symbol - } - - after_outer: - // Disallow patterns with a trailing ',' or with two ',' next to each other - auto grouping1 = static_cast<int16_t> (result.groupingSizes & 0xffff); - auto grouping2 = static_cast<int16_t> ((result.groupingSizes >> 16) & 0xffff); - auto grouping3 = static_cast<int16_t> ((result.groupingSizes >> 32) & 0xffff); - if (grouping1 == 0 && grouping2 != -1) { - state.toParseException(u"Trailing grouping separator is invalid"); - status = U_UNEXPECTED_TOKEN; - return; - } - if (grouping2 == 0 && grouping3 != -1) { - state.toParseException(u"Grouping width of zero is invalid"); - status = U_PATTERN_SYNTAX_ERROR; - return; - } -} - -void ParsedPatternInfo::consumeFractionFormat(UErrorCode& status) { - // Convenience reference: - ParsedSubpatternInfo& result = *currentSubpattern; - - int32_t zeroCounter = 0; - while (true) { - switch (state.peek()) { - case u'#': - result.widthExceptAffixes += 1; - result.fractionHashSigns += 1; - result.fractionTotal += 1; - zeroCounter++; - break; - - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - if (result.fractionHashSigns > 0) { - state.toParseException(u"0 cannot follow # after decimal point"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.fractionNumerals += 1; - result.fractionTotal += 1; - if (state.peek() == u'0') { - zeroCounter++; - } else { - result.rounding - .appendDigit(static_cast<int8_t>(state.peek() - u'0'), zeroCounter, false); - zeroCounter = 0; - } - break; - - default: - return; - } - state.next(); // consume the symbol - } -} - -void ParsedPatternInfo::consumeExponent(UErrorCode& status) { - // Convenience reference: - ParsedSubpatternInfo& result = *currentSubpattern; - - if (state.peek() != u'E') { - return; - } - if ((result.groupingSizes & 0xffff0000L) != 0xffff0000L) { - state.toParseException(u"Cannot have grouping separator in scientific notation"); - status = U_MALFORMED_EXPONENTIAL_PATTERN; - return; - } - state.next(); // consume the E - result.widthExceptAffixes++; - if (state.peek() == u'+') { - state.next(); // consume the + - result.exponentHasPlusSign = true; - result.widthExceptAffixes++; - } - while (state.peek() == u'0') { - state.next(); // consume the 0 - result.exponentZeros += 1; - result.widthExceptAffixes++; - } -} - -/////////////////////////////////////////////////// -/// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// -/////////////////////////////////////////////////// - -void PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern, - DecimalFormatProperties& properties, - IgnoreRounding ignoreRounding, UErrorCode& status) { - if (pattern.length() == 0) { - // Backwards compatibility requires that we reset to the default values. - // TODO: Only overwrite the properties that "saveToProperties" normally touches? - properties.clear(); - return; - } - - ParsedPatternInfo patternInfo; - parseToPatternInfo(pattern, patternInfo, status); - if (U_FAILURE(status)) { return; } - patternInfoToProperties(properties, patternInfo, ignoreRounding, status); -} - -void -PatternParser::patternInfoToProperties(DecimalFormatProperties& properties, ParsedPatternInfo& patternInfo, - IgnoreRounding _ignoreRounding, UErrorCode& status) { - // Translate from PatternParseResult to Properties. - // Note that most data from "negative" is ignored per the specification of DecimalFormat. - - const ParsedSubpatternInfo& positive = patternInfo.positive; - - bool ignoreRounding; - if (_ignoreRounding == IGNORE_ROUNDING_NEVER) { - ignoreRounding = false; - } else if (_ignoreRounding == IGNORE_ROUNDING_IF_CURRENCY) { - ignoreRounding = positive.hasCurrencySign; - } else { - U_ASSERT(_ignoreRounding == IGNORE_ROUNDING_ALWAYS); - ignoreRounding = true; - } - - // Grouping settings - auto grouping1 = static_cast<int16_t> (positive.groupingSizes & 0xffff); - auto grouping2 = static_cast<int16_t> ((positive.groupingSizes >> 16) & 0xffff); - auto grouping3 = static_cast<int16_t> ((positive.groupingSizes >> 32) & 0xffff); - if (grouping2 != -1) { - properties.groupingSize = grouping1; - properties.groupingUsed = true; - } else { - properties.groupingSize = -1; - properties.groupingUsed = false; - } - if (grouping3 != -1) { - properties.secondaryGroupingSize = grouping2; - } else { - properties.secondaryGroupingSize = -1; - } - - // For backwards compatibility, require that the pattern emit at least one min digit. - int minInt, minFrac; - if (positive.integerTotal == 0 && positive.fractionTotal > 0) { - // patterns like ".##" - minInt = 0; - minFrac = uprv_max(1, positive.fractionNumerals); - } else if (positive.integerNumerals == 0 && positive.fractionNumerals == 0) { - // patterns like "#.##" - minInt = 1; - minFrac = 0; - } else { - minInt = positive.integerNumerals; - minFrac = positive.fractionNumerals; - } - - // Rounding settings - // Don't set basic rounding when there is a currency sign; defer to CurrencyUsage - if (positive.integerAtSigns > 0) { - properties.minimumFractionDigits = -1; - properties.maximumFractionDigits = -1; - properties.roundingIncrement = 0.0; - properties.minimumSignificantDigits = positive.integerAtSigns; - properties.maximumSignificantDigits = positive.integerAtSigns + positive.integerTrailingHashSigns; - } else if (!positive.rounding.isZero()) { - if (!ignoreRounding) { - properties.minimumFractionDigits = minFrac; - properties.maximumFractionDigits = positive.fractionTotal; - properties.roundingIncrement = positive.rounding.toDouble(); - } else { - properties.minimumFractionDigits = -1; - properties.maximumFractionDigits = -1; - properties.roundingIncrement = 0.0; - } - properties.minimumSignificantDigits = -1; - properties.maximumSignificantDigits = -1; - } else { - if (!ignoreRounding) { - properties.minimumFractionDigits = minFrac; - properties.maximumFractionDigits = positive.fractionTotal; - properties.roundingIncrement = 0.0; - } else { - properties.minimumFractionDigits = -1; - properties.maximumFractionDigits = -1; - properties.roundingIncrement = 0.0; - } - properties.minimumSignificantDigits = -1; - properties.maximumSignificantDigits = -1; - } - - // If the pattern ends with a '.' then force the decimal point. - if (positive.hasDecimal && positive.fractionTotal == 0) { - properties.decimalSeparatorAlwaysShown = true; - } else { - properties.decimalSeparatorAlwaysShown = false; - } - - // Scientific notation settings - if (positive.exponentZeros > 0) { - properties.exponentSignAlwaysShown = positive.exponentHasPlusSign; - properties.minimumExponentDigits = positive.exponentZeros; - if (positive.integerAtSigns == 0) { - // patterns without '@' can define max integer digits, used for engineering notation - properties.minimumIntegerDigits = positive.integerNumerals; - properties.maximumIntegerDigits = positive.integerTotal; - } else { - // patterns with '@' cannot define max integer digits - properties.minimumIntegerDigits = 1; - properties.maximumIntegerDigits = -1; - } - } else { - properties.exponentSignAlwaysShown = false; - properties.minimumExponentDigits = -1; - properties.minimumIntegerDigits = minInt; - properties.maximumIntegerDigits = -1; - } - - // Compute the affix patterns (required for both padding and affixes) - UnicodeString posPrefix = patternInfo.getString(AffixPatternProvider::AFFIX_PREFIX); - UnicodeString posSuffix = patternInfo.getString(0); - - // Padding settings - if (positive.hasPadding) { - // The width of the positive prefix and suffix templates are included in the padding - int paddingWidth = positive.widthExceptAffixes + - AffixUtils::estimateLength(posPrefix, status) + - AffixUtils::estimateLength(posSuffix, status); - properties.formatWidth = paddingWidth; - UnicodeString rawPaddingString = patternInfo.getString(AffixPatternProvider::AFFIX_PADDING); - if (rawPaddingString.length() == 1) { - properties.padString = rawPaddingString; - } else if (rawPaddingString.length() == 2) { - if (rawPaddingString.charAt(0) == u'\'') { - properties.padString.setTo(u"'", -1); - } else { - properties.padString = rawPaddingString; - } - } else { - properties.padString = UnicodeString(rawPaddingString, 1, rawPaddingString.length() - 2); - } - properties.padPosition = positive.paddingLocation; - } else { - properties.formatWidth = -1; - properties.padString.setToBogus(); - properties.padPosition.nullify(); - } - - // Set the affixes - // Always call the setter, even if the prefixes are empty, especially in the case of the - // negative prefix pattern, to prevent default values from overriding the pattern. - properties.positivePrefixPattern = posPrefix; - properties.positiveSuffixPattern = posSuffix; - if (patternInfo.fHasNegativeSubpattern) { - properties.negativePrefixPattern = patternInfo.getString( - AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN | AffixPatternProvider::AFFIX_PREFIX); - properties.negativeSuffixPattern = patternInfo.getString( - AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN); - } else { - properties.negativePrefixPattern.setToBogus(); - properties.negativeSuffixPattern.setToBogus(); - } - - // Set the magnitude multiplier - if (positive.hasPercentSign) { - properties.magnitudeMultiplier = 2; - } else if (positive.hasPerMilleSign) { - properties.magnitudeMultiplier = 3; - } else { - properties.magnitudeMultiplier = 0; - } -} - -/////////////////////////////////////////////////////////////////// -/// End PatternStringParser.java; begin PatternStringUtils.java /// -/////////////////////////////////////////////////////////////////// - -UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties& properties, - UErrorCode& status) { - UnicodeString sb; - - // Convenience references - // The uprv_min() calls prevent DoS - int dosMax = 100; - int groupingSize = uprv_min(properties.secondaryGroupingSize, dosMax); - int firstGroupingSize = uprv_min(properties.groupingSize, dosMax); - int paddingWidth = uprv_min(properties.formatWidth, dosMax); - NullableValue<PadPosition> paddingLocation = properties.padPosition; - UnicodeString paddingString = properties.padString; - int minInt = uprv_max(uprv_min(properties.minimumIntegerDigits, dosMax), 0); - int maxInt = uprv_min(properties.maximumIntegerDigits, dosMax); - int minFrac = uprv_max(uprv_min(properties.minimumFractionDigits, dosMax), 0); - int maxFrac = uprv_min(properties.maximumFractionDigits, dosMax); - int minSig = uprv_min(properties.minimumSignificantDigits, dosMax); - int maxSig = uprv_min(properties.maximumSignificantDigits, dosMax); - bool alwaysShowDecimal = properties.decimalSeparatorAlwaysShown; - int exponentDigits = uprv_min(properties.minimumExponentDigits, dosMax); - bool exponentShowPlusSign = properties.exponentSignAlwaysShown; - UnicodeString pp = properties.positivePrefix; - UnicodeString ppp = properties.positivePrefixPattern; - UnicodeString ps = properties.positiveSuffix; - UnicodeString psp = properties.positiveSuffixPattern; - UnicodeString np = properties.negativePrefix; - UnicodeString npp = properties.negativePrefixPattern; - UnicodeString ns = properties.negativeSuffix; - UnicodeString nsp = properties.negativeSuffixPattern; - - // Prefixes - if (!ppp.isBogus()) { - sb.append(ppp); - } - sb.append(AffixUtils::escape(pp)); - int afterPrefixPos = sb.length(); - - // Figure out the grouping sizes. - int grouping1, grouping2, grouping; - if (groupingSize != uprv_min(dosMax, -1) && firstGroupingSize != uprv_min(dosMax, -1) && - groupingSize != firstGroupingSize) { - grouping = groupingSize; - grouping1 = groupingSize; - grouping2 = firstGroupingSize; - } else if (groupingSize != uprv_min(dosMax, -1)) { - grouping = groupingSize; - grouping1 = 0; - grouping2 = groupingSize; - } else if (firstGroupingSize != uprv_min(dosMax, -1)) { - grouping = groupingSize; - grouping1 = 0; - grouping2 = firstGroupingSize; - } else { - grouping = 0; - grouping1 = 0; - grouping2 = 0; - } - int groupingLength = grouping1 + grouping2 + 1; - - // Figure out the digits we need to put in the pattern. - double roundingInterval = properties.roundingIncrement; - UnicodeString digitsString; - int digitsStringScale = 0; - if (maxSig != uprv_min(dosMax, -1)) { - // Significant Digits. - while (digitsString.length() < minSig) { - digitsString.append(u'@'); - } - while (digitsString.length() < maxSig) { - digitsString.append(u'#'); - } - } else if (roundingInterval != 0.0) { - // Rounding Interval. - digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval); - // TODO: Check for DoS here? - DecimalQuantity incrementQuantity; - incrementQuantity.setToDouble(roundingInterval); - incrementQuantity.adjustMagnitude(-digitsStringScale); - incrementQuantity.roundToMagnitude(0, kDefaultMode, status); - UnicodeString str = incrementQuantity.toPlainString(); - if (str.charAt(0) == u'-') { - // TODO: Unsupported operation exception or fail silently? - digitsString.append(str, 1, str.length() - 1); - } else { - digitsString.append(str); - } - } - while (digitsString.length() + digitsStringScale < minInt) { - digitsString.insert(0, u'0'); - } - while (-digitsStringScale < minFrac) { - digitsString.append(u'0'); - digitsStringScale--; - } - - // Write the digits to the string builder - int m0 = uprv_max(groupingLength, digitsString.length() + digitsStringScale); - m0 = (maxInt != dosMax) ? uprv_max(maxInt, m0) - 1 : m0 - 1; - int mN = (maxFrac != dosMax) ? uprv_min(-maxFrac, digitsStringScale) : digitsStringScale; - for (int magnitude = m0; magnitude >= mN; magnitude--) { - int di = digitsString.length() + digitsStringScale - magnitude - 1; - if (di < 0 || di >= digitsString.length()) { - sb.append(u'#'); - } else { - sb.append(digitsString.charAt(di)); - } - if (magnitude > grouping2 && grouping > 0 && (magnitude - grouping2) % grouping == 0) { - sb.append(u','); - } else if (magnitude > 0 && magnitude == grouping2) { - sb.append(u','); - } else if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) { - sb.append(u'.'); - } - } - - // Exponential notation - if (exponentDigits != uprv_min(dosMax, -1)) { - sb.append(u'E'); - if (exponentShowPlusSign) { - sb.append(u'+'); - } - for (int i = 0; i < exponentDigits; i++) { - sb.append(u'0'); - } - } - - // Suffixes - int beforeSuffixPos = sb.length(); - if (!psp.isBogus()) { - sb.append(psp); - } - sb.append(AffixUtils::escape(ps)); - - // Resolve Padding - if (paddingWidth != -1 && !paddingLocation.isNull()) { - while (paddingWidth - sb.length() > 0) { - sb.insert(afterPrefixPos, u'#'); - beforeSuffixPos++; - } - int addedLength; - switch (paddingLocation.get(status)) { - case PadPosition::UNUM_PAD_BEFORE_PREFIX: - addedLength = escapePaddingString(paddingString, sb, 0, status); - sb.insert(0, u'*'); - afterPrefixPos += addedLength + 1; - beforeSuffixPos += addedLength + 1; - break; - case PadPosition::UNUM_PAD_AFTER_PREFIX: - addedLength = escapePaddingString(paddingString, sb, afterPrefixPos, status); - sb.insert(afterPrefixPos, u'*'); - afterPrefixPos += addedLength + 1; - beforeSuffixPos += addedLength + 1; - break; - case PadPosition::UNUM_PAD_BEFORE_SUFFIX: - escapePaddingString(paddingString, sb, beforeSuffixPos, status); - sb.insert(beforeSuffixPos, u'*'); - break; - case PadPosition::UNUM_PAD_AFTER_SUFFIX: - sb.append(u'*'); - escapePaddingString(paddingString, sb, sb.length(), status); - break; - } - if (U_FAILURE(status)) { return sb; } - } - - // Negative affixes - // Ignore if the negative prefix pattern is "-" and the negative suffix is empty - if (!np.isBogus() || !ns.isBogus() || (npp.isBogus() && !nsp.isBogus()) || - (!npp.isBogus() && (npp.length() != 1 || npp.charAt(0) != u'-' || nsp.length() != 0))) { - sb.append(u';'); - if (!npp.isBogus()) { - sb.append(npp); - } - sb.append(AffixUtils::escape(np)); - // Copy the positive digit format into the negative. - // This is optional; the pattern is the same as if '#' were appended here instead. - // NOTE: It is not safe to append the UnicodeString to itself, so we need to copy. - // See http://bugs.icu-project.org/trac/ticket/13707 - UnicodeString copy(sb); - sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos); - if (!nsp.isBogus()) { - sb.append(nsp); - } - sb.append(AffixUtils::escape(ns)); - } - - return sb; -} - -int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, - UErrorCode& status) { - (void) status; - if (input.length() == 0) { - input.setTo(kFallbackPaddingString, -1); - } - int startLength = output.length(); - if (input.length() == 1) { - if (input.compare(u"'", -1) == 0) { - output.insert(startIndex, u"''", -1); - } else { - output.insert(startIndex, input); - } - } else { - output.insert(startIndex, u'\''); - int offset = 1; - for (int i = 0; i < input.length(); i++) { - // it's okay to deal in chars here because the quote mark is the only interesting thing. - char16_t ch = input.charAt(i); - if (ch == u'\'') { - output.insert(startIndex + offset, u"''", -1); - offset += 2; - } else { - output.insert(startIndex + offset, ch); - offset += 1; - } - } - output.insert(startIndex + offset, u'\''); - } - return output.length() - startLength; -} - -UnicodeString -PatternStringUtils::convertLocalized(const UnicodeString& input, const DecimalFormatSymbols& symbols, - bool toLocalized, UErrorCode& status) { - // Construct a table of strings to be converted between localized and standard. - static constexpr int32_t LEN = 21; - UnicodeString table[LEN][2]; - int standIdx = toLocalized ? 0 : 1; - int localIdx = toLocalized ? 1 : 0; - table[0][standIdx] = u"%"; - table[0][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPercentSymbol); - table[1][standIdx] = u"‰"; - table[1][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); - table[2][standIdx] = u"."; - table[2][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - table[3][standIdx] = u","; - table[3][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); - table[4][standIdx] = u"-"; - table[4][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - table[5][standIdx] = u"+"; - table[5][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - table[6][standIdx] = u";"; - table[6][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); - table[7][standIdx] = u"@"; - table[7][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol); - table[8][standIdx] = u"E"; - table[8][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); - table[9][standIdx] = u"*"; - table[9][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol); - table[10][standIdx] = u"#"; - table[10][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kDigitSymbol); - for (int i = 0; i < 10; i++) { - table[11 + i][standIdx] = u'0' + i; - table[11 + i][localIdx] = symbols.getConstDigitSymbol(i); - } - - // Special case: quotes are NOT allowed to be in any localIdx strings. - // Substitute them with '’' instead. - for (int32_t i = 0; i < LEN; i++) { - table[i][localIdx].findAndReplace(u'\'', u'’'); - } - - // Iterate through the string and convert. - // State table: - // 0 => base state - // 1 => first char inside a quoted sequence in input and output string - // 2 => inside a quoted sequence in input and output string - // 3 => first char after a close quote in input string; - // close quote still needs to be written to output string - // 4 => base state in input string; inside quoted sequence in output string - // 5 => first char inside a quoted sequence in input string; - // inside quoted sequence in output string - UnicodeString result; - int state = 0; - for (int offset = 0; offset < input.length(); offset++) { - UChar ch = input.charAt(offset); - - // Handle a quote character (state shift) - if (ch == u'\'') { - if (state == 0) { - result.append(u'\''); - state = 1; - continue; - } else if (state == 1) { - result.append(u'\''); - state = 0; - continue; - } else if (state == 2) { - state = 3; - continue; - } else if (state == 3) { - result.append(u'\''); - result.append(u'\''); - state = 1; - continue; - } else if (state == 4) { - state = 5; - continue; - } else { - U_ASSERT(state == 5); - result.append(u'\''); - result.append(u'\''); - state = 4; - continue; - } - } - - if (state == 0 || state == 3 || state == 4) { - for (auto& pair : table) { - // Perform a greedy match on this symbol string - UnicodeString temp = input.tempSubString(offset, pair[0].length()); - if (temp == pair[0]) { - // Skip ahead past this region for the next iteration - offset += pair[0].length() - 1; - if (state == 3 || state == 4) { - result.append(u'\''); - state = 0; - } - result.append(pair[1]); - goto continue_outer; - } - } - // No replacement found. Check if a special quote is necessary - for (auto& pair : table) { - UnicodeString temp = input.tempSubString(offset, pair[1].length()); - if (temp == pair[1]) { - if (state == 0) { - result.append(u'\''); - state = 4; - } - result.append(ch); - goto continue_outer; - } - } - // Still nothing. Copy the char verbatim. (Add a close quote if necessary) - if (state == 3 || state == 4) { - result.append(u'\''); - state = 0; - } - result.append(ch); - } else { - U_ASSERT(state == 1 || state == 2 || state == 5); - result.append(ch); - state = 2; - } - continue_outer:; - } - // Resolve final quotes - if (state == 3 || state == 4) { - result.append(u'\''); - state = 0; - } - if (state != 0) { - // Malformed localized pattern: unterminated quote - status = U_PATTERN_SYNTAX_ERROR; - } - return result; -} - -void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix, - int8_t signum, UNumberSignDisplay signDisplay, - StandardPlural::Form plural, - bool perMilleReplacesPercent, UnicodeString& output) { - - // Should the output render '+' where '-' would normally appear in the pattern? - bool plusReplacesMinusSign = signum != -1 && ( - signDisplay == UNUM_SIGN_ALWAYS || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS || ( - signum == 1 && ( - signDisplay == UNUM_SIGN_EXCEPT_ZERO || - signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO))) && - patternInfo.positiveHasPlusSign() == false; - - // Should we use the affix from the negative subpattern? (If not, we will use the positive - // subpattern.) - bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() && ( - signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign)); - - // Resolve the flags for the affix pattern. - int flags = 0; - if (useNegativeAffixPattern) { - flags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN; - } - if (isPrefix) { - flags |= AffixPatternProvider::AFFIX_PREFIX; - } - if (plural != StandardPlural::Form::COUNT) { - U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural)); - flags |= plural; - } - - // Should we prepend a sign to the pattern? - bool prependSign; - if (!isPrefix || useNegativeAffixPattern) { - prependSign = false; - } else if (signum == -1) { - prependSign = signDisplay != UNUM_SIGN_NEVER; - } else { - prependSign = plusReplacesMinusSign; - } - - // Compute the length of the affix pattern. - int length = patternInfo.length(flags) + (prependSign ? 1 : 0); - - // Finally, set the result into the StringBuilder. - output.remove(); - for (int index = 0; index < length; index++) { - char16_t candidate; - if (prependSign && index == 0) { - candidate = u'-'; - } else if (prependSign) { - candidate = patternInfo.charAt(flags, index - 1); - } else { - candidate = patternInfo.charAt(flags, index); - } - if (plusReplacesMinusSign && candidate == u'-') { - candidate = u'+'; - } - if (perMilleReplacesPercent && candidate == u'%') { - candidate = u'‰'; - } - output.append(candidate); - } -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ |