summaryrefslogtreecommitdiff
path: root/deps/node/deps/icu-small/source/i18n/number_patternstring.cpp
diff options
context:
space:
mode:
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.cpp1070
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 */