diff options
Diffstat (limited to 'intl/icu/source/i18n/decimalformatpattern.cpp')
-rw-r--r-- | intl/icu/source/i18n/decimalformatpattern.cpp | 654 |
1 files changed, 654 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/decimalformatpattern.cpp b/intl/icu/source/i18n/decimalformatpattern.cpp new file mode 100644 index 000000000..af1ccd29b --- /dev/null +++ b/intl/icu/source/i18n/decimalformatpattern.cpp @@ -0,0 +1,654 @@ +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2015, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#include "uassert.h" +#include "decimalformatpattern.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/dcfmtsym.h" +#include "unicode/format.h" +#include "unicode/utf16.h" +#include "decimalformatpatternimpl.h" + + +#ifdef FMT_DEBUG +#define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x); +#else +#define debug(x) +#endif + +U_NAMESPACE_BEGIN + +// TODO: Travis Keep: Copied from numfmt.cpp +static int32_t kDoubleIntegerDigits = 309; +static int32_t kDoubleFractionDigits = 340; + + +// TODO: Travis Keep: Copied from numfmt.cpp +static int32_t gDefaultMaxIntegerDigits = 2000000000; + +// TODO: Travis Keep: This function was copied from format.cpp +static void syntaxError(const UnicodeString& pattern, + int32_t pos, + UParseError& parseError) { + parseError.offset = pos; + parseError.line=0; // we are not using line number + + // for pre-context + int32_t start = (pos < U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1 + /* subtract 1 so that we have room for null*/)); + int32_t stop = pos; + pattern.extract(start,stop-start,parseError.preContext,0); + //null terminate the buffer + parseError.preContext[stop-start] = 0; + + //for post-context + start = pos+1; + stop = ((pos+U_PARSE_CONTEXT_LEN)<=pattern.length()) ? (pos+(U_PARSE_CONTEXT_LEN-1)) : + pattern.length(); + pattern.extract(start,stop-start,parseError.postContext,0); + //null terminate the buffer + parseError.postContext[stop-start]= 0; +} + +DecimalFormatPattern::DecimalFormatPattern() + : fMinimumIntegerDigits(1), + fMaximumIntegerDigits(gDefaultMaxIntegerDigits), + fMinimumFractionDigits(0), + fMaximumFractionDigits(3), + fUseSignificantDigits(FALSE), + fMinimumSignificantDigits(1), + fMaximumSignificantDigits(6), + fUseExponentialNotation(FALSE), + fMinExponentDigits(0), + fExponentSignAlwaysShown(FALSE), + fCurrencySignCount(fgCurrencySignCountZero), + fGroupingUsed(TRUE), + fGroupingSize(0), + fGroupingSize2(0), + fMultiplier(1), + fDecimalSeparatorAlwaysShown(FALSE), + fFormatWidth(0), + fRoundingIncrementUsed(FALSE), + fRoundingIncrement(), + fPad(kDefaultPad), + fNegPatternsBogus(TRUE), + fPosPatternsBogus(TRUE), + fNegPrefixPattern(), + fNegSuffixPattern(), + fPosPrefixPattern(), + fPosSuffixPattern(), + fPadPosition(DecimalFormatPattern::kPadBeforePrefix) { +} + + +DecimalFormatPatternParser::DecimalFormatPatternParser() : + fZeroDigit(kPatternZeroDigit), + fSigDigit(kPatternSignificantDigit), + fGroupingSeparator((UChar)kPatternGroupingSeparator), + fDecimalSeparator((UChar)kPatternDecimalSeparator), + fPercent((UChar)kPatternPercent), + fPerMill((UChar)kPatternPerMill), + fDigit((UChar)kPatternDigit), + fSeparator((UChar)kPatternSeparator), + fExponent((UChar)kPatternExponent), + fPlus((UChar)kPatternPlus), + fMinus((UChar)kPatternMinus), + fPadEscape((UChar)kPatternPadEscape) { +} + +void DecimalFormatPatternParser::useSymbols( + const DecimalFormatSymbols& symbols) { + fZeroDigit = symbols.getConstSymbol( + DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); + fSigDigit = symbols.getConstSymbol( + DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); + fGroupingSeparator = symbols.getConstSymbol( + DecimalFormatSymbols::kGroupingSeparatorSymbol); + fDecimalSeparator = symbols.getConstSymbol( + DecimalFormatSymbols::kDecimalSeparatorSymbol); + fPercent = symbols.getConstSymbol( + DecimalFormatSymbols::kPercentSymbol); + fPerMill = symbols.getConstSymbol( + DecimalFormatSymbols::kPerMillSymbol); + fDigit = symbols.getConstSymbol( + DecimalFormatSymbols::kDigitSymbol); + fSeparator = symbols.getConstSymbol( + DecimalFormatSymbols::kPatternSeparatorSymbol); + fExponent = symbols.getConstSymbol( + DecimalFormatSymbols::kExponentialSymbol); + fPlus = symbols.getConstSymbol( + DecimalFormatSymbols::kPlusSignSymbol); + fMinus = symbols.getConstSymbol( + DecimalFormatSymbols::kMinusSignSymbol); + fPadEscape = symbols.getConstSymbol( + DecimalFormatSymbols::kPadEscapeSymbol); +} + +void +DecimalFormatPatternParser::applyPatternWithoutExpandAffix( + const UnicodeString& pattern, + DecimalFormatPattern& out, + UParseError& parseError, + UErrorCode& status) { + if (U_FAILURE(status)) + { + return; + } + out = DecimalFormatPattern(); + + // Clear error struct + parseError.offset = -1; + parseError.preContext[0] = parseError.postContext[0] = (UChar)0; + + // TODO: Travis Keep: This won't always work. + UChar nineDigit = (UChar)(fZeroDigit + 9); + int32_t digitLen = fDigit.length(); + int32_t groupSepLen = fGroupingSeparator.length(); + int32_t decimalSepLen = fDecimalSeparator.length(); + + int32_t pos = 0; + int32_t patLen = pattern.length(); + // Part 0 is the positive pattern. Part 1, if present, is the negative + // pattern. + for (int32_t part=0; part<2 && pos<patLen; ++part) { + // The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix, + // 2=suffix, 3=prefix in quote, 4=suffix in quote. Subpart 0 is + // between the prefix and suffix, and consists of pattern + // characters. In the prefix and suffix, percent, perMill, and + // currency symbols are recognized and translated. + int32_t subpart = 1, sub0Start = 0, sub0Limit = 0, sub2Limit = 0; + + // It's important that we don't change any fields of this object + // prematurely. We set the following variables for the multiplier, + // grouping, etc., and then only change the actual object fields if + // everything parses correctly. This also lets us register + // the data from part 0 and ignore the part 1, except for the + // prefix and suffix. + UnicodeString prefix; + UnicodeString suffix; + int32_t decimalPos = -1; + int32_t multiplier = 1; + int32_t digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0, sigDigitCount = 0; + int8_t groupingCount = -1; + int8_t groupingCount2 = -1; + int32_t padPos = -1; + UChar32 padChar = 0; + int32_t roundingPos = -1; + DigitList roundingInc; + int8_t expDigits = -1; + UBool expSignAlways = FALSE; + + // The affix is either the prefix or the suffix. + UnicodeString* affix = &prefix; + + int32_t start = pos; + UBool isPartDone = FALSE; + UChar32 ch; + + for (; !isPartDone && pos < patLen; ) { + // Todo: account for surrogate pairs + ch = pattern.char32At(pos); + switch (subpart) { + case 0: // Pattern proper subpart (between prefix & suffix) + // Process the digits, decimal, and grouping characters. We + // record five pieces of information. We expect the digits + // to occur in the pattern ####00.00####, and we record the + // number of left digits, zero (central) digits, and right + // digits. The position of the last grouping character is + // recorded (should be somewhere within the first two blocks + // of characters), as is the position of the decimal point, + // if any (should be in the zero digits). If there is no + // decimal point, then there should be no right digits. + if (pattern.compare(pos, digitLen, fDigit) == 0) { + if (zeroDigitCount > 0 || sigDigitCount > 0) { + ++digitRightCount; + } else { + ++digitLeftCount; + } + if (groupingCount >= 0 && decimalPos < 0) { + ++groupingCount; + } + pos += digitLen; + } else if ((ch >= fZeroDigit && ch <= nineDigit) || + ch == fSigDigit) { + if (digitRightCount > 0) { + // Unexpected '0' + debug("Unexpected '0'") + status = U_UNEXPECTED_TOKEN; + syntaxError(pattern,pos,parseError); + return; + } + if (ch == fSigDigit) { + ++sigDigitCount; + } else { + if (ch != fZeroDigit && roundingPos < 0) { + roundingPos = digitLeftCount + zeroDigitCount; + } + if (roundingPos >= 0) { + roundingInc.append((char)(ch - fZeroDigit + '0')); + } + ++zeroDigitCount; + } + if (groupingCount >= 0 && decimalPos < 0) { + ++groupingCount; + } + pos += U16_LENGTH(ch); + } else if (pattern.compare(pos, groupSepLen, fGroupingSeparator) == 0) { + if (decimalPos >= 0) { + // Grouping separator after decimal + debug("Grouping separator after decimal") + status = U_UNEXPECTED_TOKEN; + syntaxError(pattern,pos,parseError); + return; + } + groupingCount2 = groupingCount; + groupingCount = 0; + pos += groupSepLen; + } else if (pattern.compare(pos, decimalSepLen, fDecimalSeparator) == 0) { + if (decimalPos >= 0) { + // Multiple decimal separators + debug("Multiple decimal separators") + status = U_MULTIPLE_DECIMAL_SEPARATORS; + syntaxError(pattern,pos,parseError); + return; + } + // Intentionally incorporate the digitRightCount, + // even though it is illegal for this to be > 0 + // at this point. We check pattern syntax below. + decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; + pos += decimalSepLen; + } else { + if (pattern.compare(pos, fExponent.length(), fExponent) == 0) { + if (expDigits >= 0) { + // Multiple exponential symbols + debug("Multiple exponential symbols") + status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; + syntaxError(pattern,pos,parseError); + return; + } + if (groupingCount >= 0) { + // Grouping separator in exponential pattern + debug("Grouping separator in exponential pattern") + status = U_MALFORMED_EXPONENTIAL_PATTERN; + syntaxError(pattern,pos,parseError); + return; + } + pos += fExponent.length(); + // Check for positive prefix + if (pos < patLen + && pattern.compare(pos, fPlus.length(), fPlus) == 0) { + expSignAlways = TRUE; + pos += fPlus.length(); + } + // Use lookahead to parse out the exponential part of the + // pattern, then jump into suffix subpart. + expDigits = 0; + while (pos < patLen && + pattern.char32At(pos) == fZeroDigit) { + ++expDigits; + pos += U16_LENGTH(fZeroDigit); + } + + // 1. Require at least one mantissa pattern digit + // 2. Disallow "#+ @" in mantissa + // 3. Require at least one exponent pattern digit + if (((digitLeftCount + zeroDigitCount) < 1 && + (sigDigitCount + digitRightCount) < 1) || + (sigDigitCount > 0 && digitLeftCount > 0) || + expDigits < 1) { + // Malformed exponential pattern + debug("Malformed exponential pattern") + status = U_MALFORMED_EXPONENTIAL_PATTERN; + syntaxError(pattern,pos,parseError); + return; + } + } + // Transition to suffix subpart + subpart = 2; // suffix subpart + affix = &suffix; + sub0Limit = pos; + continue; + } + break; + case 1: // Prefix subpart + case 2: // Suffix subpart + // Process the prefix / suffix characters + // Process unquoted characters seen in prefix or suffix + // subpart. + + // Several syntax characters implicitly begins the + // next subpart if we are in the prefix; otherwise + // they are illegal if unquoted. + if (!pattern.compare(pos, digitLen, fDigit) || + !pattern.compare(pos, groupSepLen, fGroupingSeparator) || + !pattern.compare(pos, decimalSepLen, fDecimalSeparator) || + (ch >= fZeroDigit && ch <= nineDigit) || + ch == fSigDigit) { + if (subpart == 1) { // prefix subpart + subpart = 0; // pattern proper subpart + sub0Start = pos; // Reprocess this character + continue; + } else { + status = U_UNQUOTED_SPECIAL; + syntaxError(pattern,pos,parseError); + return; + } + } else if (ch == kCurrencySign) { + affix->append(kQuote); // Encode currency + // Use lookahead to determine if the currency sign is + // doubled or not. + U_ASSERT(U16_LENGTH(kCurrencySign) == 1); + if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { + affix->append(kCurrencySign); + ++pos; // Skip over the doubled character + if ((pos+1) < pattern.length() && + pattern[pos+1] == kCurrencySign) { + affix->append(kCurrencySign); + ++pos; // Skip over the doubled character + out.fCurrencySignCount = fgCurrencySignCountInPluralFormat; + } else { + out.fCurrencySignCount = fgCurrencySignCountInISOFormat; + } + } else { + out.fCurrencySignCount = fgCurrencySignCountInSymbolFormat; + } + // Fall through to append(ch) + } else if (ch == kQuote) { + // A quote outside quotes indicates either the opening + // quote or two quotes, which is a quote literal. That is, + // we have the first quote in 'do' or o''clock. + U_ASSERT(U16_LENGTH(kQuote) == 1); + ++pos; + if (pos < pattern.length() && pattern[pos] == kQuote) { + affix->append(kQuote); // Encode quote + // Fall through to append(ch) + } else { + subpart += 2; // open quote + continue; + } + } else if (pattern.compare(pos, fSeparator.length(), fSeparator) == 0) { + // Don't allow separators in the prefix, and don't allow + // separators in the second pattern (part == 1). + if (subpart == 1 || part == 1) { + // Unexpected separator + debug("Unexpected separator") + status = U_UNEXPECTED_TOKEN; + syntaxError(pattern,pos,parseError); + return; + } + sub2Limit = pos; + isPartDone = TRUE; // Go to next part + pos += fSeparator.length(); + break; + } else if (pattern.compare(pos, fPercent.length(), fPercent) == 0) { + // Next handle characters which are appended directly. + if (multiplier != 1) { + // Too many percent/perMill characters + debug("Too many percent characters") + status = U_MULTIPLE_PERCENT_SYMBOLS; + syntaxError(pattern,pos,parseError); + return; + } + affix->append(kQuote); // Encode percent/perMill + affix->append(kPatternPercent); // Use unlocalized pattern char + multiplier = 100; + pos += fPercent.length(); + break; + } else if (pattern.compare(pos, fPerMill.length(), fPerMill) == 0) { + // Next handle characters which are appended directly. + if (multiplier != 1) { + // Too many percent/perMill characters + debug("Too many perMill characters") + status = U_MULTIPLE_PERMILL_SYMBOLS; + syntaxError(pattern,pos,parseError); + return; + } + affix->append(kQuote); // Encode percent/perMill + affix->append(kPatternPerMill); // Use unlocalized pattern char + multiplier = 1000; + pos += fPerMill.length(); + break; + } else if (pattern.compare(pos, fPadEscape.length(), fPadEscape) == 0) { + if (padPos >= 0 || // Multiple pad specifiers + (pos+1) == pattern.length()) { // Nothing after padEscape + debug("Multiple pad specifiers") + status = U_MULTIPLE_PAD_SPECIFIERS; + syntaxError(pattern,pos,parseError); + return; + } + padPos = pos; + pos += fPadEscape.length(); + padChar = pattern.char32At(pos); + pos += U16_LENGTH(padChar); + break; + } else if (pattern.compare(pos, fMinus.length(), fMinus) == 0) { + affix->append(kQuote); // Encode minus + affix->append(kPatternMinus); + pos += fMinus.length(); + break; + } else if (pattern.compare(pos, fPlus.length(), fPlus) == 0) { + affix->append(kQuote); // Encode plus + affix->append(kPatternPlus); + pos += fPlus.length(); + break; + } + // Unquoted, non-special characters fall through to here, as + // well as other code which needs to append something to the + // affix. + affix->append(ch); + pos += U16_LENGTH(ch); + break; + case 3: // Prefix subpart, in quote + case 4: // Suffix subpart, in quote + // A quote within quotes indicates either the closing + // quote or two quotes, which is a quote literal. That is, + // we have the second quote in 'do' or 'don''t'. + if (ch == kQuote) { + ++pos; + if (pos < pattern.length() && pattern[pos] == kQuote) { + affix->append(kQuote); // Encode quote + // Fall through to append(ch) + } else { + subpart -= 2; // close quote + continue; + } + } + affix->append(ch); + pos += U16_LENGTH(ch); + break; + } + } + + if (sub0Limit == 0) { + sub0Limit = pattern.length(); + } + + if (sub2Limit == 0) { + sub2Limit = pattern.length(); + } + + /* Handle patterns with no '0' pattern character. These patterns + * are legal, but must be recodified to make sense. "##.###" -> + * "#0.###". ".###" -> ".0##". + * + * We allow patterns of the form "####" to produce a zeroDigitCount + * of zero (got that?); although this seems like it might make it + * possible for format() to produce empty strings, format() checks + * for this condition and outputs a zero digit in this situation. + * Having a zeroDigitCount of zero yields a minimum integer digits + * of zero, which allows proper round-trip patterns. We don't want + * "#" to become "#0" when toPattern() is called (even though that's + * what it really is, semantically). + */ + if (zeroDigitCount == 0 && sigDigitCount == 0 && + digitLeftCount > 0 && decimalPos >= 0) { + // Handle "###.###" and "###." and ".###" + int n = decimalPos; + if (n == 0) + ++n; // Handle ".###" + digitRightCount = digitLeftCount - n; + digitLeftCount = n - 1; + zeroDigitCount = 1; + } + + // Do syntax checking on the digits, decimal points, and quotes. + if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) || + (decimalPos >= 0 && + (sigDigitCount > 0 || + decimalPos < digitLeftCount || + decimalPos > (digitLeftCount + zeroDigitCount))) || + groupingCount == 0 || groupingCount2 == 0 || + (sigDigitCount > 0 && zeroDigitCount > 0) || + subpart > 2) + { // subpart > 2 == unmatched quote + debug("Syntax error") + status = U_PATTERN_SYNTAX_ERROR; + syntaxError(pattern,pos,parseError); + return; + } + + // Make sure pad is at legal position before or after affix. + if (padPos >= 0) { + if (padPos == start) { + padPos = DecimalFormatPattern::kPadBeforePrefix; + } else if (padPos+2 == sub0Start) { + padPos = DecimalFormatPattern::kPadAfterPrefix; + } else if (padPos == sub0Limit) { + padPos = DecimalFormatPattern::kPadBeforeSuffix; + } else if (padPos+2 == sub2Limit) { + padPos = DecimalFormatPattern::kPadAfterSuffix; + } else { + // Illegal pad position + debug("Illegal pad position") + status = U_ILLEGAL_PAD_POSITION; + syntaxError(pattern,pos,parseError); + return; + } + } + + if (part == 0) { + out.fPosPatternsBogus = FALSE; + out.fPosPrefixPattern = prefix; + out.fPosSuffixPattern = suffix; + out.fNegPatternsBogus = TRUE; + out.fNegPrefixPattern.remove(); + out.fNegSuffixPattern.remove(); + + out.fUseExponentialNotation = (expDigits >= 0); + if (out.fUseExponentialNotation) { + out.fMinExponentDigits = expDigits; + } + out.fExponentSignAlwaysShown = expSignAlways; + int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; + // The effectiveDecimalPos is the position the decimal is at or + // would be at if there is no decimal. Note that if + // decimalPos<0, then digitTotalCount == digitLeftCount + + // zeroDigitCount. + int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; + UBool isSigDig = (sigDigitCount > 0); + out.fUseSignificantDigits = isSigDig; + if (isSigDig) { + out.fMinimumSignificantDigits = sigDigitCount; + out.fMaximumSignificantDigits = sigDigitCount + digitRightCount; + } else { + int32_t minInt = effectiveDecimalPos - digitLeftCount; + out.fMinimumIntegerDigits = minInt; + out.fMaximumIntegerDigits = out.fUseExponentialNotation + ? digitLeftCount + out.fMinimumIntegerDigits + : gDefaultMaxIntegerDigits; + out.fMaximumFractionDigits = decimalPos >= 0 + ? (digitTotalCount - decimalPos) : 0; + out.fMinimumFractionDigits = decimalPos >= 0 + ? (digitLeftCount + zeroDigitCount - decimalPos) : 0; + } + out.fGroupingUsed = groupingCount > 0; + out.fGroupingSize = (groupingCount > 0) ? groupingCount : 0; + out.fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) + ? groupingCount2 : 0; + out.fMultiplier = multiplier; + out.fDecimalSeparatorAlwaysShown = decimalPos == 0 + || decimalPos == digitTotalCount; + if (padPos >= 0) { + out.fPadPosition = (DecimalFormatPattern::EPadPosition) padPos; + // To compute the format width, first set up sub0Limit - + // sub0Start. Add in prefix/suffix length later. + + // fFormatWidth = prefix.length() + suffix.length() + + // sub0Limit - sub0Start; + out.fFormatWidth = sub0Limit - sub0Start; + out.fPad = padChar; + } else { + out.fFormatWidth = 0; + } + if (roundingPos >= 0) { + out.fRoundingIncrementUsed = TRUE; + roundingInc.setDecimalAt(effectiveDecimalPos - roundingPos); + out.fRoundingIncrement = roundingInc; + } else { + out.fRoundingIncrementUsed = FALSE; + } + } else { + out.fNegPatternsBogus = FALSE; + out.fNegPrefixPattern = prefix; + out.fNegSuffixPattern = suffix; + } + } + + if (pattern.length() == 0) { + out.fNegPatternsBogus = TRUE; + out.fNegPrefixPattern.remove(); + out.fNegSuffixPattern.remove(); + out.fPosPatternsBogus = FALSE; + out.fPosPrefixPattern.remove(); + out.fPosSuffixPattern.remove(); + + out.fMinimumIntegerDigits = 0; + out.fMaximumIntegerDigits = kDoubleIntegerDigits; + out.fMinimumFractionDigits = 0; + out.fMaximumFractionDigits = kDoubleFractionDigits; + + out.fUseExponentialNotation = FALSE; + out.fCurrencySignCount = fgCurrencySignCountZero; + out.fGroupingUsed = FALSE; + out.fGroupingSize = 0; + out.fGroupingSize2 = 0; + out.fMultiplier = 1; + out.fDecimalSeparatorAlwaysShown = FALSE; + out.fFormatWidth = 0; + out.fRoundingIncrementUsed = FALSE; + } + + // If there was no negative pattern, or if the negative pattern is + // identical to the positive pattern, then prepend the minus sign to the + // positive pattern to form the negative pattern. + if (out.fNegPatternsBogus || + (out.fNegPrefixPattern == out.fPosPrefixPattern + && out.fNegSuffixPattern == out.fPosSuffixPattern)) { + out.fNegPatternsBogus = FALSE; + out.fNegSuffixPattern = out.fPosSuffixPattern; + out.fNegPrefixPattern.remove(); + out.fNegPrefixPattern.append(kQuote).append(kPatternMinus) + .append(out.fPosPrefixPattern); + } + // TODO: Deprecate/Remove out.fNegSuffixPattern and 3 other fields. + AffixPattern::parseAffixString( + out.fNegSuffixPattern, out.fNegSuffixAffix, status); + AffixPattern::parseAffixString( + out.fPosSuffixPattern, out.fPosSuffixAffix, status); + AffixPattern::parseAffixString( + out.fNegPrefixPattern, out.fNegPrefixAffix, status); + AffixPattern::parseAffixString( + out.fPosPrefixPattern, out.fPosPrefixAffix, status); +} + +U_NAMESPACE_END + +#endif /* !UCONFIG_NO_FORMATTING */ |