1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 1997-2015, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
11 #include "decimalformatpattern.h"
13 #if !UCONFIG_NO_FORMATTING
15 #include "unicode/dcfmtsym.h"
16 #include "unicode/format.h"
17 #include "unicode/utf16.h"
18 #include "decimalformatpatternimpl.h"
22 #define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x);
29 // TODO: Travis Keep: Copied from numfmt.cpp
30 static int32_t kDoubleIntegerDigits
= 309;
31 static int32_t kDoubleFractionDigits
= 340;
34 // TODO: Travis Keep: Copied from numfmt.cpp
35 static int32_t gDefaultMaxIntegerDigits
= 2000000000;
37 // TODO: Travis Keep: This function was copied from format.cpp
38 static void syntaxError(const UnicodeString
& pattern
,
40 UParseError
& parseError
) {
41 parseError
.offset
= pos
;
42 parseError
.line
=0; // we are not using line number
45 int32_t start
= (pos
< U_PARSE_CONTEXT_LEN
)? 0 : (pos
- (U_PARSE_CONTEXT_LEN
-1
46 /* subtract 1 so that we have room for null*/));
48 pattern
.extract(start
,stop
-start
,parseError
.preContext
,0);
49 //null terminate the buffer
50 parseError
.preContext
[stop
-start
] = 0;
53 start
= pattern
.moveIndex32(pos
, 1);
54 stop
= pos
+ U_PARSE_CONTEXT_LEN
- 1;
55 if (stop
> pattern
.length()) {
56 stop
= pattern
.length();
58 pattern
.extract(start
, stop
- start
, parseError
.postContext
, 0);
59 //null terminate the buffer
60 parseError
.postContext
[stop
-start
]= 0;
63 DecimalFormatPattern::DecimalFormatPattern()
64 : fMinimumIntegerDigits(1),
65 fMaximumIntegerDigits(gDefaultMaxIntegerDigits
),
66 fMinimumFractionDigits(0),
67 fMaximumFractionDigits(3),
68 fUseSignificantDigits(FALSE
),
69 fMinimumSignificantDigits(1),
70 fMaximumSignificantDigits(6),
71 fUseExponentialNotation(FALSE
),
72 fMinExponentDigits(0),
73 fExponentSignAlwaysShown(FALSE
),
74 fCurrencySignCount(fgCurrencySignCountZero
),
79 fDecimalSeparatorAlwaysShown(FALSE
),
81 fRoundingIncrementUsed(FALSE
),
84 fNegPatternsBogus(TRUE
),
85 fPosPatternsBogus(TRUE
),
90 fPadPosition(DecimalFormatPattern::kPadBeforePrefix
) {
94 DecimalFormatPatternParser::DecimalFormatPatternParser() :
95 fZeroDigit(kPatternZeroDigit
),
96 fSigDigit(kPatternSignificantDigit
),
97 fGroupingSeparator((UChar
)kPatternGroupingSeparator
),
98 fDecimalSeparator((UChar
)kPatternDecimalSeparator
),
99 fPercent((UChar
)kPatternPercent
),
100 fPerMill((UChar
)kPatternPerMill
),
101 fDigit((UChar
)kPatternDigit
),
102 fSeparator((UChar
)kPatternSeparator
),
103 fExponent((UChar
)kPatternExponent
),
104 fPlus((UChar
)kPatternPlus
),
105 fMinus((UChar
)kPatternMinus
),
106 fPadEscape((UChar
)kPatternPadEscape
) {
109 void DecimalFormatPatternParser::useSymbols(
110 const DecimalFormatSymbols
& symbols
) {
111 fZeroDigit
= symbols
.getConstSymbol(
112 DecimalFormatSymbols::kZeroDigitSymbol
).char32At(0);
113 fSigDigit
= symbols
.getConstSymbol(
114 DecimalFormatSymbols::kSignificantDigitSymbol
).char32At(0);
115 fGroupingSeparator
= symbols
.getConstSymbol(
116 DecimalFormatSymbols::kGroupingSeparatorSymbol
);
117 fDecimalSeparator
= symbols
.getConstSymbol(
118 DecimalFormatSymbols::kDecimalSeparatorSymbol
);
119 fPercent
= symbols
.getConstSymbol(
120 DecimalFormatSymbols::kPercentSymbol
);
121 fPerMill
= symbols
.getConstSymbol(
122 DecimalFormatSymbols::kPerMillSymbol
);
123 fDigit
= symbols
.getConstSymbol(
124 DecimalFormatSymbols::kDigitSymbol
);
125 fSeparator
= symbols
.getConstSymbol(
126 DecimalFormatSymbols::kPatternSeparatorSymbol
);
127 fExponent
= symbols
.getConstSymbol(
128 DecimalFormatSymbols::kExponentialSymbol
);
129 fPlus
= symbols
.getConstSymbol(
130 DecimalFormatSymbols::kPlusSignSymbol
);
131 fMinus
= symbols
.getConstSymbol(
132 DecimalFormatSymbols::kMinusSignSymbol
);
133 fPadEscape
= symbols
.getConstSymbol(
134 DecimalFormatSymbols::kPadEscapeSymbol
);
138 DecimalFormatPatternParser::applyPatternWithoutExpandAffix(
139 const UnicodeString
& pattern
,
140 DecimalFormatPattern
& out
,
141 UParseError
& parseError
,
142 UErrorCode
& status
) {
143 if (U_FAILURE(status
))
147 out
= DecimalFormatPattern();
149 // Clear error struct
150 parseError
.offset
= -1;
151 parseError
.preContext
[0] = parseError
.postContext
[0] = (UChar
)0;
153 // TODO: Travis Keep: This won't always work.
154 UChar nineDigit
= (UChar
)(fZeroDigit
+ 9);
155 int32_t digitLen
= fDigit
.length();
156 int32_t groupSepLen
= fGroupingSeparator
.length();
157 int32_t decimalSepLen
= fDecimalSeparator
.length();
160 int32_t patLen
= pattern
.length();
161 // Part 0 is the positive pattern. Part 1, if present, is the negative
163 for (int32_t part
=0; part
<2 && pos
<patLen
; ++part
) {
164 // The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix,
165 // 2=suffix, 3=prefix in quote, 4=suffix in quote. Subpart 0 is
166 // between the prefix and suffix, and consists of pattern
167 // characters. In the prefix and suffix, percent, perMill, and
168 // currency symbols are recognized and translated.
169 int32_t subpart
= 1, sub0Start
= 0, sub0Limit
= 0, sub2Limit
= 0;
171 // It's important that we don't change any fields of this object
172 // prematurely. We set the following variables for the multiplier,
173 // grouping, etc., and then only change the actual object fields if
174 // everything parses correctly. This also lets us register
175 // the data from part 0 and ignore the part 1, except for the
176 // prefix and suffix.
177 UnicodeString prefix
;
178 UnicodeString suffix
;
179 int32_t decimalPos
= -1;
180 int32_t multiplier
= 1;
181 int32_t digitLeftCount
= 0, zeroDigitCount
= 0, digitRightCount
= 0, sigDigitCount
= 0;
182 int8_t groupingCount
= -1;
183 int8_t groupingCount2
= -1;
186 int32_t roundingPos
= -1;
187 DigitList roundingInc
;
188 int8_t expDigits
= -1;
189 UBool expSignAlways
= FALSE
;
191 // The affix is either the prefix or the suffix.
192 UnicodeString
* affix
= &prefix
;
195 UBool isPartDone
= FALSE
;
198 for (; !isPartDone
&& pos
< patLen
; ) {
199 // Todo: account for surrogate pairs
200 ch
= pattern
.char32At(pos
);
202 case 0: // Pattern proper subpart (between prefix & suffix)
203 // Process the digits, decimal, and grouping characters. We
204 // record five pieces of information. We expect the digits
205 // to occur in the pattern ####00.00####, and we record the
206 // number of left digits, zero (central) digits, and right
207 // digits. The position of the last grouping character is
208 // recorded (should be somewhere within the first two blocks
209 // of characters), as is the position of the decimal point,
210 // if any (should be in the zero digits). If there is no
211 // decimal point, then there should be no right digits.
212 if (pattern
.compare(pos
, digitLen
, fDigit
) == 0) {
213 if (zeroDigitCount
> 0 || sigDigitCount
> 0) {
218 if (groupingCount
>= 0 && decimalPos
< 0) {
222 } else if ((ch
>= fZeroDigit
&& ch
<= nineDigit
) ||
224 if (digitRightCount
> 0) {
226 debug("Unexpected '0'")
227 status
= U_UNEXPECTED_TOKEN
;
228 syntaxError(pattern
,pos
,parseError
);
231 if (ch
== fSigDigit
) {
234 if (ch
!= fZeroDigit
&& roundingPos
< 0) {
235 roundingPos
= digitLeftCount
+ zeroDigitCount
;
237 if (roundingPos
>= 0) {
238 roundingInc
.append((char)(ch
- fZeroDigit
+ '0'));
242 if (groupingCount
>= 0 && decimalPos
< 0) {
245 pos
+= U16_LENGTH(ch
);
246 } else if (pattern
.compare(pos
, groupSepLen
, fGroupingSeparator
) == 0) {
247 if (decimalPos
>= 0) {
248 // Grouping separator after decimal
249 debug("Grouping separator after decimal")
250 status
= U_UNEXPECTED_TOKEN
;
251 syntaxError(pattern
,pos
,parseError
);
254 groupingCount2
= groupingCount
;
257 } else if (pattern
.compare(pos
, decimalSepLen
, fDecimalSeparator
) == 0) {
258 if (decimalPos
>= 0) {
259 // Multiple decimal separators
260 debug("Multiple decimal separators")
261 status
= U_MULTIPLE_DECIMAL_SEPARATORS
;
262 syntaxError(pattern
,pos
,parseError
);
265 // Intentionally incorporate the digitRightCount,
266 // even though it is illegal for this to be > 0
267 // at this point. We check pattern syntax below.
268 decimalPos
= digitLeftCount
+ zeroDigitCount
+ digitRightCount
;
269 pos
+= decimalSepLen
;
271 if (pattern
.compare(pos
, fExponent
.length(), fExponent
) == 0) {
272 if (expDigits
>= 0) {
273 // Multiple exponential symbols
274 debug("Multiple exponential symbols")
275 status
= U_MULTIPLE_EXPONENTIAL_SYMBOLS
;
276 syntaxError(pattern
,pos
,parseError
);
279 if (groupingCount
>= 0) {
280 // Grouping separator in exponential pattern
281 debug("Grouping separator in exponential pattern")
282 status
= U_MALFORMED_EXPONENTIAL_PATTERN
;
283 syntaxError(pattern
,pos
,parseError
);
286 pos
+= fExponent
.length();
287 // Check for positive prefix
289 && pattern
.compare(pos
, fPlus
.length(), fPlus
) == 0) {
290 expSignAlways
= TRUE
;
291 pos
+= fPlus
.length();
293 // Use lookahead to parse out the exponential part of the
294 // pattern, then jump into suffix subpart.
296 while (pos
< patLen
&&
297 pattern
.char32At(pos
) == fZeroDigit
) {
299 pos
+= U16_LENGTH(fZeroDigit
);
302 // 1. Require at least one mantissa pattern digit
303 // 2. Disallow "#+ @" in mantissa
304 // 3. Require at least one exponent pattern digit
305 if (((digitLeftCount
+ zeroDigitCount
) < 1 &&
306 (sigDigitCount
+ digitRightCount
) < 1) ||
307 (sigDigitCount
> 0 && digitLeftCount
> 0) ||
309 // Malformed exponential pattern
310 debug("Malformed exponential pattern")
311 status
= U_MALFORMED_EXPONENTIAL_PATTERN
;
312 syntaxError(pattern
,pos
,parseError
);
316 // Transition to suffix subpart
317 subpart
= 2; // suffix subpart
323 case 1: // Prefix subpart
324 case 2: // Suffix subpart
325 // Process the prefix / suffix characters
326 // Process unquoted characters seen in prefix or suffix
329 // Several syntax characters implicitly begins the
330 // next subpart if we are in the prefix; otherwise
331 // they are illegal if unquoted.
332 if (!pattern
.compare(pos
, digitLen
, fDigit
) ||
333 !pattern
.compare(pos
, groupSepLen
, fGroupingSeparator
) ||
334 !pattern
.compare(pos
, decimalSepLen
, fDecimalSeparator
) ||
335 (ch
>= fZeroDigit
&& ch
<= nineDigit
) ||
337 if (subpart
== 1) { // prefix subpart
338 subpart
= 0; // pattern proper subpart
339 sub0Start
= pos
; // Reprocess this character
342 status
= U_UNQUOTED_SPECIAL
;
343 syntaxError(pattern
,pos
,parseError
);
346 } else if (ch
== kCurrencySign
) {
347 affix
->append(kQuote
); // Encode currency
348 // Use lookahead to determine if the currency sign is
350 U_ASSERT(U16_LENGTH(kCurrencySign
) == 1);
351 if ((pos
+1) < pattern
.length() && pattern
[pos
+1] == kCurrencySign
) {
352 affix
->append(kCurrencySign
);
353 ++pos
; // Skip over the doubled character
354 if ((pos
+1) < pattern
.length() &&
355 pattern
[pos
+1] == kCurrencySign
) {
356 affix
->append(kCurrencySign
);
357 ++pos
; // Skip over the doubled character
358 out
.fCurrencySignCount
= fgCurrencySignCountInPluralFormat
;
360 out
.fCurrencySignCount
= fgCurrencySignCountInISOFormat
;
363 out
.fCurrencySignCount
= fgCurrencySignCountInSymbolFormat
;
365 // Fall through to append(ch)
366 } else if (ch
== kQuote
) {
367 // A quote outside quotes indicates either the opening
368 // quote or two quotes, which is a quote literal. That is,
369 // we have the first quote in 'do' or o''clock.
370 U_ASSERT(U16_LENGTH(kQuote
) == 1);
372 if (pos
< pattern
.length() && pattern
[pos
] == kQuote
) {
373 affix
->append(kQuote
); // Encode quote
374 // Fall through to append(ch)
376 subpart
+= 2; // open quote
379 } else if (pattern
.compare(pos
, fSeparator
.length(), fSeparator
) == 0) {
380 // Don't allow separators in the prefix, and don't allow
381 // separators in the second pattern (part == 1).
382 if (subpart
== 1 || part
== 1) {
383 // Unexpected separator
384 debug("Unexpected separator")
385 status
= U_UNEXPECTED_TOKEN
;
386 syntaxError(pattern
,pos
,parseError
);
390 isPartDone
= TRUE
; // Go to next part
391 pos
+= fSeparator
.length();
393 } else if (pattern
.compare(pos
, fPercent
.length(), fPercent
) == 0) {
394 // Next handle characters which are appended directly.
395 if (multiplier
!= 1) {
396 // Too many percent/perMill characters
397 debug("Too many percent characters")
398 status
= U_MULTIPLE_PERCENT_SYMBOLS
;
399 syntaxError(pattern
,pos
,parseError
);
402 affix
->append(kQuote
); // Encode percent/perMill
403 affix
->append(kPatternPercent
); // Use unlocalized pattern char
405 pos
+= fPercent
.length();
407 } else if (pattern
.compare(pos
, fPerMill
.length(), fPerMill
) == 0) {
408 // Next handle characters which are appended directly.
409 if (multiplier
!= 1) {
410 // Too many percent/perMill characters
411 debug("Too many perMill characters")
412 status
= U_MULTIPLE_PERMILL_SYMBOLS
;
413 syntaxError(pattern
,pos
,parseError
);
416 affix
->append(kQuote
); // Encode percent/perMill
417 affix
->append(kPatternPerMill
); // Use unlocalized pattern char
419 pos
+= fPerMill
.length();
421 } else if (pattern
.compare(pos
, fPadEscape
.length(), fPadEscape
) == 0) {
422 if (padPos
>= 0 || // Multiple pad specifiers
423 (pos
+1) == pattern
.length()) { // Nothing after padEscape
424 debug("Multiple pad specifiers")
425 status
= U_MULTIPLE_PAD_SPECIFIERS
;
426 syntaxError(pattern
,pos
,parseError
);
430 pos
+= fPadEscape
.length();
431 padChar
= pattern
.char32At(pos
);
432 pos
+= U16_LENGTH(padChar
);
434 } else if (pattern
.compare(pos
, fMinus
.length(), fMinus
) == 0) {
435 affix
->append(kQuote
); // Encode minus
436 affix
->append(kPatternMinus
);
437 pos
+= fMinus
.length();
439 } else if (pattern
.compare(pos
, fPlus
.length(), fPlus
) == 0) {
440 affix
->append(kQuote
); // Encode plus
441 affix
->append(kPatternPlus
);
442 pos
+= fPlus
.length();
445 // Unquoted, non-special characters fall through to here, as
446 // well as other code which needs to append something to the
449 pos
+= U16_LENGTH(ch
);
451 case 3: // Prefix subpart, in quote
452 case 4: // Suffix subpart, in quote
453 // A quote within quotes indicates either the closing
454 // quote or two quotes, which is a quote literal. That is,
455 // we have the second quote in 'do' or 'don''t'.
458 if (pos
< pattern
.length() && pattern
[pos
] == kQuote
) {
459 affix
->append(kQuote
); // Encode quote
460 // Fall through to append(ch)
462 subpart
-= 2; // close quote
467 pos
+= U16_LENGTH(ch
);
472 if (sub0Limit
== 0) {
473 sub0Limit
= pattern
.length();
476 if (sub2Limit
== 0) {
477 sub2Limit
= pattern
.length();
480 /* Handle patterns with no '0' pattern character. These patterns
481 * are legal, but must be recodified to make sense. "##.###" ->
482 * "#0.###". ".###" -> ".0##".
484 * We allow patterns of the form "####" to produce a zeroDigitCount
485 * of zero (got that?); although this seems like it might make it
486 * possible for format() to produce empty strings, format() checks
487 * for this condition and outputs a zero digit in this situation.
488 * Having a zeroDigitCount of zero yields a minimum integer digits
489 * of zero, which allows proper round-trip patterns. We don't want
490 * "#" to become "#0" when toPattern() is called (even though that's
491 * what it really is, semantically).
493 if (zeroDigitCount
== 0 && sigDigitCount
== 0 &&
494 digitLeftCount
> 0 && decimalPos
>= 0) {
495 // Handle "###.###" and "###." and ".###"
498 ++n
; // Handle ".###"
499 digitRightCount
= digitLeftCount
- n
;
500 digitLeftCount
= n
- 1;
504 // Do syntax checking on the digits, decimal points, and quotes.
505 if ((decimalPos
< 0 && digitRightCount
> 0 && sigDigitCount
== 0) ||
507 (sigDigitCount
> 0 ||
508 decimalPos
< digitLeftCount
||
509 decimalPos
> (digitLeftCount
+ zeroDigitCount
))) ||
510 groupingCount
== 0 || groupingCount2
== 0 ||
511 (sigDigitCount
> 0 && zeroDigitCount
> 0) ||
513 { // subpart > 2 == unmatched quote
514 debug("Syntax error")
515 status
= U_PATTERN_SYNTAX_ERROR
;
516 syntaxError(pattern
,pos
,parseError
);
520 // Make sure pad is at legal position before or after affix.
522 if (padPos
== start
) {
523 padPos
= DecimalFormatPattern::kPadBeforePrefix
;
524 } else if (padPos
+2 == sub0Start
) {
525 padPos
= DecimalFormatPattern::kPadAfterPrefix
;
526 } else if (padPos
== sub0Limit
) {
527 padPos
= DecimalFormatPattern::kPadBeforeSuffix
;
528 } else if (padPos
+2 == sub2Limit
) {
529 padPos
= DecimalFormatPattern::kPadAfterSuffix
;
531 // Illegal pad position
532 debug("Illegal pad position")
533 status
= U_ILLEGAL_PAD_POSITION
;
534 syntaxError(pattern
,pos
,parseError
);
540 out
.fPosPatternsBogus
= FALSE
;
541 out
.fPosPrefixPattern
= prefix
;
542 out
.fPosSuffixPattern
= suffix
;
543 out
.fNegPatternsBogus
= TRUE
;
544 out
.fNegPrefixPattern
.remove();
545 out
.fNegSuffixPattern
.remove();
547 out
.fUseExponentialNotation
= (expDigits
>= 0);
548 if (out
.fUseExponentialNotation
) {
549 out
.fMinExponentDigits
= expDigits
;
551 out
.fExponentSignAlwaysShown
= expSignAlways
;
552 int32_t digitTotalCount
= digitLeftCount
+ zeroDigitCount
+ digitRightCount
;
553 // The effectiveDecimalPos is the position the decimal is at or
554 // would be at if there is no decimal. Note that if
555 // decimalPos<0, then digitTotalCount == digitLeftCount +
557 int32_t effectiveDecimalPos
= decimalPos
>= 0 ? decimalPos
: digitTotalCount
;
558 UBool isSigDig
= (sigDigitCount
> 0);
559 out
.fUseSignificantDigits
= isSigDig
;
561 out
.fMinimumSignificantDigits
= sigDigitCount
;
562 out
.fMaximumSignificantDigits
= sigDigitCount
+ digitRightCount
;
564 int32_t minInt
= effectiveDecimalPos
- digitLeftCount
;
565 out
.fMinimumIntegerDigits
= minInt
;
566 out
.fMaximumIntegerDigits
= out
.fUseExponentialNotation
567 ? digitLeftCount
+ out
.fMinimumIntegerDigits
568 : gDefaultMaxIntegerDigits
;
569 out
.fMaximumFractionDigits
= decimalPos
>= 0
570 ? (digitTotalCount
- decimalPos
) : 0;
571 out
.fMinimumFractionDigits
= decimalPos
>= 0
572 ? (digitLeftCount
+ zeroDigitCount
- decimalPos
) : 0;
574 out
.fGroupingUsed
= groupingCount
> 0;
575 out
.fGroupingSize
= (groupingCount
> 0) ? groupingCount
: 0;
576 out
.fGroupingSize2
= (groupingCount2
> 0 && groupingCount2
!= groupingCount
)
577 ? groupingCount2
: 0;
578 out
.fMultiplier
= multiplier
;
579 out
.fDecimalSeparatorAlwaysShown
= decimalPos
== 0
580 || decimalPos
== digitTotalCount
;
582 out
.fPadPosition
= (DecimalFormatPattern::EPadPosition
) padPos
;
583 // To compute the format width, first set up sub0Limit -
584 // sub0Start. Add in prefix/suffix length later.
586 // fFormatWidth = prefix.length() + suffix.length() +
587 // sub0Limit - sub0Start;
588 out
.fFormatWidth
= sub0Limit
- sub0Start
;
591 out
.fFormatWidth
= 0;
593 if (roundingPos
>= 0) {
594 out
.fRoundingIncrementUsed
= TRUE
;
595 roundingInc
.setDecimalAt(effectiveDecimalPos
- roundingPos
);
596 out
.fRoundingIncrement
= roundingInc
;
598 out
.fRoundingIncrementUsed
= FALSE
;
601 out
.fNegPatternsBogus
= FALSE
;
602 out
.fNegPrefixPattern
= prefix
;
603 out
.fNegSuffixPattern
= suffix
;
607 if (pattern
.length() == 0) {
608 out
.fNegPatternsBogus
= TRUE
;
609 out
.fNegPrefixPattern
.remove();
610 out
.fNegSuffixPattern
.remove();
611 out
.fPosPatternsBogus
= FALSE
;
612 out
.fPosPrefixPattern
.remove();
613 out
.fPosSuffixPattern
.remove();
615 out
.fMinimumIntegerDigits
= 0;
616 out
.fMaximumIntegerDigits
= kDoubleIntegerDigits
;
617 out
.fMinimumFractionDigits
= 0;
618 out
.fMaximumFractionDigits
= kDoubleFractionDigits
;
620 out
.fUseExponentialNotation
= FALSE
;
621 out
.fCurrencySignCount
= fgCurrencySignCountZero
;
622 out
.fGroupingUsed
= FALSE
;
623 out
.fGroupingSize
= 0;
624 out
.fGroupingSize2
= 0;
626 out
.fDecimalSeparatorAlwaysShown
= FALSE
;
627 out
.fFormatWidth
= 0;
628 out
.fRoundingIncrementUsed
= FALSE
;
631 // If there was no negative pattern, or if the negative pattern is
632 // identical to the positive pattern, then prepend the minus sign to the
633 // positive pattern to form the negative pattern.
634 if (out
.fNegPatternsBogus
||
635 (out
.fNegPrefixPattern
== out
.fPosPrefixPattern
636 && out
.fNegSuffixPattern
== out
.fPosSuffixPattern
)) {
637 out
.fNegPatternsBogus
= FALSE
;
638 out
.fNegSuffixPattern
= out
.fPosSuffixPattern
;
639 out
.fNegPrefixPattern
.remove();
640 out
.fNegPrefixPattern
.append(kQuote
).append(kPatternMinus
)
641 .append(out
.fPosPrefixPattern
);
643 // TODO: Deprecate/Remove out.fNegSuffixPattern and 3 other fields.
644 AffixPattern::parseAffixString(
645 out
.fNegSuffixPattern
, out
.fNegSuffixAffix
, status
);
646 AffixPattern::parseAffixString(
647 out
.fPosSuffixPattern
, out
.fPosSuffixAffix
, status
);
648 AffixPattern::parseAffixString(
649 out
.fNegPrefixPattern
, out
.fNegPrefixAffix
, status
);
650 AffixPattern::parseAffixString(
651 out
.fPosPrefixPattern
, out
.fPosPrefixAffix
, status
);
656 #endif /* !UCONFIG_NO_FORMATTING */