1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.impl.number; 5 6 import ohos.global.icu.impl.number.Padder.PadPosition; 7 8 /** Implements a recursive descent parser for decimal format patterns. 9 * @hide exposed on OHOS*/ 10 public class PatternStringParser { 11 12 public static final int IGNORE_ROUNDING_NEVER = 0; 13 public static final int IGNORE_ROUNDING_IF_CURRENCY = 1; 14 public static final int IGNORE_ROUNDING_ALWAYS = 2; 15 16 /** 17 * Runs the recursive descent parser on the given pattern string, returning a data structure with raw 18 * information about the pattern string. 19 * 20 * <p> 21 * To obtain a more useful form of the data, consider using {@link #parseToProperties} instead. 22 * 23 * @param patternString 24 * The LDML decimal format pattern (Excel-style pattern) to parse. 25 * @return The results of the parse. 26 */ parseToPatternInfo(String patternString)27 public static ParsedPatternInfo parseToPatternInfo(String patternString) { 28 ParserState state = new ParserState(patternString); 29 ParsedPatternInfo result = new ParsedPatternInfo(patternString); 30 consumePattern(state, result); 31 return result; 32 } 33 34 /** 35 * Parses a pattern string into a new property bag. 36 * 37 * @param pattern 38 * The pattern string, like "#,##0.00" 39 * @param ignoreRounding 40 * Whether to leave out rounding information (minFrac, maxFrac, and rounding increment) 41 * when parsing the pattern. This may be desirable if a custom rounding mode, such as 42 * CurrencyUsage, is to be used instead. One of 43 * {@link PatternStringParser#IGNORE_ROUNDING_ALWAYS}, 44 * {@link PatternStringParser#IGNORE_ROUNDING_IF_CURRENCY}, or 45 * {@link PatternStringParser#IGNORE_ROUNDING_NEVER}. 46 * @return A property bag object. 47 * @throws IllegalArgumentException 48 * If there is a syntax error in the pattern string. 49 */ parseToProperties(String pattern, int ignoreRounding)50 public static DecimalFormatProperties parseToProperties(String pattern, int ignoreRounding) { 51 DecimalFormatProperties properties = new DecimalFormatProperties(); 52 parseToExistingPropertiesImpl(pattern, properties, ignoreRounding); 53 return properties; 54 } 55 parseToProperties(String pattern)56 public static DecimalFormatProperties parseToProperties(String pattern) { 57 return parseToProperties(pattern, PatternStringParser.IGNORE_ROUNDING_NEVER); 58 } 59 60 /** 61 * Parses a pattern string into an existing property bag. All properties that can be encoded into a 62 * pattern string will be overwritten with either their default value or with the value coming from 63 * the pattern string. Properties that cannot be encoded into a pattern string, such as rounding 64 * mode, are not modified. 65 * 66 * @param pattern 67 * The pattern string, like "#,##0.00" 68 * @param properties 69 * The property bag object to overwrite. 70 * @param ignoreRounding 71 * See {@link #parseToProperties(String pattern, int ignoreRounding)}. 72 * @throws IllegalArgumentException 73 * If there was a syntax error in the pattern string. 74 */ parseToExistingProperties( String pattern, DecimalFormatProperties properties, int ignoreRounding)75 public static void parseToExistingProperties( 76 String pattern, 77 DecimalFormatProperties properties, 78 int ignoreRounding) { 79 parseToExistingPropertiesImpl(pattern, properties, ignoreRounding); 80 } 81 parseToExistingProperties(String pattern, DecimalFormatProperties properties)82 public static void parseToExistingProperties(String pattern, DecimalFormatProperties properties) { 83 parseToExistingProperties(pattern, properties, PatternStringParser.IGNORE_ROUNDING_NEVER); 84 } 85 86 /** 87 * Contains raw information about the parsed decimal format pattern string. 88 * @hide exposed on OHOS 89 */ 90 public static class ParsedPatternInfo implements AffixPatternProvider { 91 public String pattern; 92 public ParsedSubpatternInfo positive; 93 public ParsedSubpatternInfo negative; 94 ParsedPatternInfo(String pattern)95 private ParsedPatternInfo(String pattern) { 96 this.pattern = pattern; 97 } 98 99 @Override charAt(int flags, int index)100 public char charAt(int flags, int index) { 101 long endpoints = getEndpoints(flags); 102 int left = (int) (endpoints & 0xffffffff); 103 int right = (int) (endpoints >>> 32); 104 if (index < 0 || index >= right - left) { 105 throw new IndexOutOfBoundsException(); 106 } 107 return pattern.charAt(left + index); 108 } 109 110 @Override length(int flags)111 public int length(int flags) { 112 return getLengthFromEndpoints(getEndpoints(flags)); 113 } 114 getLengthFromEndpoints(long endpoints)115 public static int getLengthFromEndpoints(long endpoints) { 116 int left = (int) (endpoints & 0xffffffff); 117 int right = (int) (endpoints >>> 32); 118 return right - left; 119 } 120 121 @Override getString(int flags)122 public String getString(int flags) { 123 long endpoints = getEndpoints(flags); 124 int left = (int) (endpoints & 0xffffffff); 125 int right = (int) (endpoints >>> 32); 126 if (left == right) { 127 return ""; 128 } 129 return pattern.substring(left, right); 130 } 131 getEndpoints(int flags)132 private long getEndpoints(int flags) { 133 boolean prefix = (flags & Flags.PREFIX) != 0; 134 boolean isNegative = (flags & Flags.NEGATIVE_SUBPATTERN) != 0; 135 boolean padding = (flags & Flags.PADDING) != 0; 136 if (isNegative && padding) { 137 return negative.paddingEndpoints; 138 } else if (padding) { 139 return positive.paddingEndpoints; 140 } else if (prefix && isNegative) { 141 return negative.prefixEndpoints; 142 } else if (prefix) { 143 return positive.prefixEndpoints; 144 } else if (isNegative) { 145 return negative.suffixEndpoints; 146 } else { 147 return positive.suffixEndpoints; 148 } 149 } 150 151 @Override positiveHasPlusSign()152 public boolean positiveHasPlusSign() { 153 return positive.hasPlusSign; 154 } 155 156 @Override hasNegativeSubpattern()157 public boolean hasNegativeSubpattern() { 158 return negative != null; 159 } 160 161 @Override negativeHasMinusSign()162 public boolean negativeHasMinusSign() { 163 return negative.hasMinusSign; 164 } 165 166 @Override hasCurrencySign()167 public boolean hasCurrencySign() { 168 return positive.hasCurrencySign || (negative != null && negative.hasCurrencySign); 169 } 170 171 @Override containsSymbolType(int type)172 public boolean containsSymbolType(int type) { 173 return AffixUtils.containsType(pattern, type); 174 } 175 176 @Override hasBody()177 public boolean hasBody() { 178 return positive.integerTotal > 0; 179 } 180 } 181 182 /** 183 * @hide exposed on OHOS 184 */ 185 public static class ParsedSubpatternInfo { 186 public long groupingSizes = 0x0000ffffffff0000L; 187 public int integerLeadingHashSigns = 0; 188 public int integerTrailingHashSigns = 0; 189 public int integerNumerals = 0; 190 public int integerAtSigns = 0; 191 public int integerTotal = 0; // for convenience 192 public int fractionNumerals = 0; 193 public int fractionHashSigns = 0; 194 public int fractionTotal = 0; // for convenience 195 public boolean hasDecimal = false; 196 public int widthExceptAffixes = 0; 197 public PadPosition paddingLocation = null; 198 public DecimalQuantity_DualStorageBCD rounding = null; 199 public boolean exponentHasPlusSign = false; 200 public int exponentZeros = 0; 201 public boolean hasPercentSign = false; 202 public boolean hasPerMilleSign = false; 203 public boolean hasCurrencySign = false; 204 public boolean hasMinusSign = false; 205 public boolean hasPlusSign = false; 206 207 public long prefixEndpoints = 0; 208 public long suffixEndpoints = 0; 209 public long paddingEndpoints = 0; 210 } 211 212 ///////////////////////////////////////////////////// 213 /// BEGIN RECURSIVE DESCENT PARSER IMPLEMENTATION /// 214 ///////////////////////////////////////////////////// 215 216 /** An internal class used for tracking the cursor during parsing of a pattern string. */ 217 private static class ParserState { 218 final String pattern; 219 int offset; 220 ParserState(String pattern)221 ParserState(String pattern) { 222 this.pattern = pattern; 223 this.offset = 0; 224 } 225 peek()226 int peek() { 227 if (offset == pattern.length()) { 228 return -1; 229 } else { 230 return pattern.codePointAt(offset); 231 } 232 } 233 next()234 int next() { 235 int codePoint = peek(); 236 offset += Character.charCount(codePoint); 237 return codePoint; 238 } 239 toParseException(String message)240 IllegalArgumentException toParseException(String message) { 241 StringBuilder sb = new StringBuilder(); 242 sb.append("Malformed pattern for ICU DecimalFormat: \""); 243 sb.append(pattern); 244 sb.append("\": "); 245 sb.append(message); 246 sb.append(" at position "); 247 sb.append(offset); 248 return new IllegalArgumentException(sb.toString()); 249 } 250 } 251 consumePattern(ParserState state, ParsedPatternInfo result)252 private static void consumePattern(ParserState state, ParsedPatternInfo result) { 253 // pattern := subpattern (';' subpattern)? 254 result.positive = new ParsedSubpatternInfo(); 255 consumeSubpattern(state, result.positive); 256 if (state.peek() == ';') { 257 state.next(); // consume the ';' 258 // Don't consume the negative subpattern if it is empty (trailing ';') 259 if (state.peek() != -1) { 260 result.negative = new ParsedSubpatternInfo(); 261 consumeSubpattern(state, result.negative); 262 } 263 } 264 if (state.peek() != -1) { 265 throw state.toParseException("Found unquoted special character"); 266 } 267 } 268 consumeSubpattern(ParserState state, ParsedSubpatternInfo result)269 private static void consumeSubpattern(ParserState state, ParsedSubpatternInfo result) { 270 // subpattern := literals? number exponent? literals? 271 consumePadding(state, result, PadPosition.BEFORE_PREFIX); 272 result.prefixEndpoints = consumeAffix(state, result); 273 consumePadding(state, result, PadPosition.AFTER_PREFIX); 274 consumeFormat(state, result); 275 consumeExponent(state, result); 276 consumePadding(state, result, PadPosition.BEFORE_SUFFIX); 277 result.suffixEndpoints = consumeAffix(state, result); 278 consumePadding(state, result, PadPosition.AFTER_SUFFIX); 279 } 280 consumePadding( ParserState state, ParsedSubpatternInfo result, PadPosition paddingLocation)281 private static void consumePadding( 282 ParserState state, 283 ParsedSubpatternInfo result, 284 PadPosition paddingLocation) { 285 if (state.peek() != '*') { 286 return; 287 } 288 if (result.paddingLocation != null) { 289 throw state.toParseException("Cannot have multiple pad specifiers"); 290 } 291 result.paddingLocation = paddingLocation; 292 state.next(); // consume the '*' 293 result.paddingEndpoints |= state.offset; 294 consumeLiteral(state); 295 result.paddingEndpoints |= ((long) state.offset) << 32; 296 } 297 consumeAffix(ParserState state, ParsedSubpatternInfo result)298 private static long consumeAffix(ParserState state, ParsedSubpatternInfo result) { 299 // literals := { literal } 300 long endpoints = state.offset; 301 outer: while (true) { 302 switch (state.peek()) { 303 case '#': 304 case '@': 305 case ';': 306 case '*': 307 case '.': 308 case ',': 309 case '0': 310 case '1': 311 case '2': 312 case '3': 313 case '4': 314 case '5': 315 case '6': 316 case '7': 317 case '8': 318 case '9': 319 case -1: 320 // Characters that cannot appear unquoted in a literal 321 break outer; 322 323 case '%': 324 result.hasPercentSign = true; 325 break; 326 327 case '‰': 328 result.hasPerMilleSign = true; 329 break; 330 331 case '¤': 332 result.hasCurrencySign = true; 333 break; 334 335 case '-': 336 result.hasMinusSign = true; 337 break; 338 339 case '+': 340 result.hasPlusSign = true; 341 break; 342 } 343 consumeLiteral(state); 344 } 345 endpoints |= ((long) state.offset) << 32; 346 return endpoints; 347 } 348 consumeLiteral(ParserState state)349 private static void consumeLiteral(ParserState state) { 350 if (state.peek() == -1) { 351 throw state.toParseException("Expected unquoted literal but found EOL"); 352 } else if (state.peek() == '\'') { 353 state.next(); // consume the starting quote 354 while (state.peek() != '\'') { 355 if (state.peek() == -1) { 356 throw state.toParseException("Expected quoted literal but found EOL"); 357 } else { 358 state.next(); // consume a quoted character 359 } 360 } 361 state.next(); // consume the ending quote 362 } else { 363 // consume a non-quoted literal character 364 state.next(); 365 } 366 } 367 consumeFormat(ParserState state, ParsedSubpatternInfo result)368 private static void consumeFormat(ParserState state, ParsedSubpatternInfo result) { 369 consumeIntegerFormat(state, result); 370 if (state.peek() == '.') { 371 state.next(); // consume the decimal point 372 result.hasDecimal = true; 373 result.widthExceptAffixes += 1; 374 consumeFractionFormat(state, result); 375 } 376 } 377 consumeIntegerFormat(ParserState state, ParsedSubpatternInfo result)378 private static void consumeIntegerFormat(ParserState state, ParsedSubpatternInfo result) { 379 outer: while (true) { 380 switch (state.peek()) { 381 case ',': 382 result.widthExceptAffixes += 1; 383 result.groupingSizes <<= 16; 384 break; 385 386 case '#': 387 if (result.integerNumerals > 0) { 388 throw state.toParseException("# cannot follow 0 before decimal point"); 389 } 390 result.widthExceptAffixes += 1; 391 result.groupingSizes += 1; 392 if (result.integerAtSigns > 0) { 393 result.integerTrailingHashSigns += 1; 394 } else { 395 result.integerLeadingHashSigns += 1; 396 } 397 result.integerTotal += 1; 398 break; 399 400 case '@': 401 if (result.integerNumerals > 0) { 402 throw state.toParseException("Cannot mix 0 and @"); 403 } 404 if (result.integerTrailingHashSigns > 0) { 405 throw state.toParseException("Cannot nest # inside of a run of @"); 406 } 407 result.widthExceptAffixes += 1; 408 result.groupingSizes += 1; 409 result.integerAtSigns += 1; 410 result.integerTotal += 1; 411 break; 412 413 case '0': 414 case '1': 415 case '2': 416 case '3': 417 case '4': 418 case '5': 419 case '6': 420 case '7': 421 case '8': 422 case '9': 423 if (result.integerAtSigns > 0) { 424 throw state.toParseException("Cannot mix @ and 0"); 425 } 426 result.widthExceptAffixes += 1; 427 result.groupingSizes += 1; 428 result.integerNumerals += 1; 429 result.integerTotal += 1; 430 if (state.peek() != '0' && result.rounding == null) { 431 result.rounding = new DecimalQuantity_DualStorageBCD(); 432 } 433 if (result.rounding != null) { 434 result.rounding.appendDigit((byte) (state.peek() - '0'), 0, true); 435 } 436 break; 437 438 default: 439 break outer; 440 } 441 state.next(); // consume the symbol 442 } 443 444 // Disallow patterns with a trailing ',' or with two ',' next to each other 445 short grouping1 = (short) (result.groupingSizes & 0xffff); 446 short grouping2 = (short) ((result.groupingSizes >>> 16) & 0xffff); 447 short grouping3 = (short) ((result.groupingSizes >>> 32) & 0xffff); 448 if (grouping1 == 0 && grouping2 != -1) { 449 throw state.toParseException("Trailing grouping separator is invalid"); 450 } 451 if (grouping2 == 0 && grouping3 != -1) { 452 throw state.toParseException("Grouping width of zero is invalid"); 453 } 454 } 455 consumeFractionFormat(ParserState state, ParsedSubpatternInfo result)456 private static void consumeFractionFormat(ParserState state, ParsedSubpatternInfo result) { 457 int zeroCounter = 0; 458 while (true) { 459 switch (state.peek()) { 460 case '#': 461 result.widthExceptAffixes += 1; 462 result.fractionHashSigns += 1; 463 result.fractionTotal += 1; 464 zeroCounter++; 465 break; 466 467 case '0': 468 case '1': 469 case '2': 470 case '3': 471 case '4': 472 case '5': 473 case '6': 474 case '7': 475 case '8': 476 case '9': 477 if (result.fractionHashSigns > 0) { 478 throw state.toParseException("0 cannot follow # after decimal point"); 479 } 480 result.widthExceptAffixes += 1; 481 result.fractionNumerals += 1; 482 result.fractionTotal += 1; 483 if (state.peek() == '0') { 484 zeroCounter++; 485 } else { 486 if (result.rounding == null) { 487 result.rounding = new DecimalQuantity_DualStorageBCD(); 488 } 489 result.rounding.appendDigit((byte) (state.peek() - '0'), zeroCounter, false); 490 zeroCounter = 0; 491 } 492 break; 493 494 default: 495 return; 496 } 497 state.next(); // consume the symbol 498 } 499 } 500 consumeExponent(ParserState state, ParsedSubpatternInfo result)501 private static void consumeExponent(ParserState state, ParsedSubpatternInfo result) { 502 if (state.peek() != 'E') { 503 return; 504 } 505 if ((result.groupingSizes & 0xffff0000L) != 0xffff0000L) { 506 throw state.toParseException("Cannot have grouping separator in scientific notation"); 507 } 508 state.next(); // consume the E 509 result.widthExceptAffixes++; 510 if (state.peek() == '+') { 511 state.next(); // consume the + 512 result.exponentHasPlusSign = true; 513 result.widthExceptAffixes++; 514 } 515 while (state.peek() == '0') { 516 state.next(); // consume the 0 517 result.exponentZeros += 1; 518 result.widthExceptAffixes++; 519 } 520 } 521 522 /////////////////////////////////////////////////// 523 /// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// 524 /////////////////////////////////////////////////// 525 parseToExistingPropertiesImpl( String pattern, DecimalFormatProperties properties, int ignoreRounding)526 private static void parseToExistingPropertiesImpl( 527 String pattern, 528 DecimalFormatProperties properties, 529 int ignoreRounding) { 530 if (pattern == null || pattern.length() == 0) { 531 // Backwards compatibility requires that we reset to the default values. 532 // TODO: Only overwrite the properties that "saveToProperties" normally touches? 533 properties.clear(); 534 return; 535 } 536 537 // TODO: Use thread locals here? 538 ParsedPatternInfo patternInfo = parseToPatternInfo(pattern); 539 patternInfoToProperties(properties, patternInfo, ignoreRounding); 540 } 541 542 /** Finalizes the temporary data stored in the ParsedPatternInfo to the Properties. */ patternInfoToProperties( DecimalFormatProperties properties, ParsedPatternInfo patternInfo, int _ignoreRounding)543 private static void patternInfoToProperties( 544 DecimalFormatProperties properties, 545 ParsedPatternInfo patternInfo, 546 int _ignoreRounding) { 547 // Translate from PatternParseResult to Properties. 548 // Note that most data from "negative" is ignored per the specification of DecimalFormat. 549 550 ParsedSubpatternInfo positive = patternInfo.positive; 551 552 boolean ignoreRounding; 553 if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_NEVER) { 554 ignoreRounding = false; 555 } else if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_IF_CURRENCY) { 556 ignoreRounding = positive.hasCurrencySign; 557 } else { 558 assert _ignoreRounding == PatternStringParser.IGNORE_ROUNDING_ALWAYS; 559 ignoreRounding = true; 560 } 561 562 // Grouping settings 563 short grouping1 = (short) (positive.groupingSizes & 0xffff); 564 short grouping2 = (short) ((positive.groupingSizes >>> 16) & 0xffff); 565 short grouping3 = (short) ((positive.groupingSizes >>> 32) & 0xffff); 566 if (grouping2 != -1) { 567 properties.setGroupingSize(grouping1); 568 properties.setGroupingUsed(true); 569 } else { 570 properties.setGroupingSize(-1); 571 properties.setGroupingUsed(false); 572 } 573 if (grouping3 != -1) { 574 properties.setSecondaryGroupingSize(grouping2); 575 } else { 576 properties.setSecondaryGroupingSize(-1); 577 } 578 579 // For backwards compatibility, require that the pattern emit at least one min digit. 580 int minInt, minFrac; 581 if (positive.integerTotal == 0 && positive.fractionTotal > 0) { 582 // patterns like ".##" 583 minInt = 0; 584 minFrac = Math.max(1, positive.fractionNumerals); 585 } else if (positive.integerNumerals == 0 && positive.fractionNumerals == 0) { 586 // patterns like "#.##" 587 minInt = 1; 588 minFrac = 0; 589 } else { 590 minInt = positive.integerNumerals; 591 minFrac = positive.fractionNumerals; 592 } 593 594 // Rounding settings 595 // Don't set basic rounding when there is a currency sign; defer to CurrencyUsage 596 if (positive.integerAtSigns > 0) { 597 properties.setMinimumFractionDigits(-1); 598 properties.setMaximumFractionDigits(-1); 599 properties.setRoundingIncrement(null); 600 properties.setMinimumSignificantDigits(positive.integerAtSigns); 601 properties.setMaximumSignificantDigits( 602 positive.integerAtSigns + positive.integerTrailingHashSigns); 603 } else if (positive.rounding != null) { 604 if (!ignoreRounding) { 605 properties.setMinimumFractionDigits(minFrac); 606 properties.setMaximumFractionDigits(positive.fractionTotal); 607 properties.setRoundingIncrement( 608 positive.rounding.toBigDecimal().setScale(positive.fractionNumerals)); 609 } else { 610 properties.setMinimumFractionDigits(-1); 611 properties.setMaximumFractionDigits(-1); 612 properties.setRoundingIncrement(null); 613 } 614 properties.setMinimumSignificantDigits(-1); 615 properties.setMaximumSignificantDigits(-1); 616 } else { 617 if (!ignoreRounding) { 618 properties.setMinimumFractionDigits(minFrac); 619 properties.setMaximumFractionDigits(positive.fractionTotal); 620 properties.setRoundingIncrement(null); 621 } else { 622 properties.setMinimumFractionDigits(-1); 623 properties.setMaximumFractionDigits(-1); 624 properties.setRoundingIncrement(null); 625 } 626 properties.setMinimumSignificantDigits(-1); 627 properties.setMaximumSignificantDigits(-1); 628 } 629 630 // If the pattern ends with a '.' then force the decimal point. 631 if (positive.hasDecimal && positive.fractionTotal == 0) { 632 properties.setDecimalSeparatorAlwaysShown(true); 633 } else { 634 properties.setDecimalSeparatorAlwaysShown(false); 635 } 636 637 // Scientific notation settings 638 if (positive.exponentZeros > 0) { 639 properties.setExponentSignAlwaysShown(positive.exponentHasPlusSign); 640 properties.setMinimumExponentDigits(positive.exponentZeros); 641 if (positive.integerAtSigns == 0) { 642 // patterns without '@' can define max integer digits, used for engineering notation 643 properties.setMinimumIntegerDigits(positive.integerNumerals); 644 properties.setMaximumIntegerDigits(positive.integerTotal); 645 } else { 646 // patterns with '@' cannot define max integer digits 647 properties.setMinimumIntegerDigits(1); 648 properties.setMaximumIntegerDigits(-1); 649 } 650 } else { 651 properties.setExponentSignAlwaysShown(false); 652 properties.setMinimumExponentDigits(-1); 653 properties.setMinimumIntegerDigits(minInt); 654 properties.setMaximumIntegerDigits(-1); 655 } 656 657 // Compute the affix patterns (required for both padding and affixes) 658 String posPrefix = patternInfo.getString(AffixPatternProvider.Flags.PREFIX); 659 String posSuffix = patternInfo.getString(0); 660 661 // Padding settings 662 if (positive.paddingLocation != null) { 663 // The width of the positive prefix and suffix templates are included in the padding 664 int paddingWidth = positive.widthExceptAffixes 665 + AffixUtils.estimateLength(posPrefix) 666 + AffixUtils.estimateLength(posSuffix); 667 properties.setFormatWidth(paddingWidth); 668 String rawPaddingString = patternInfo.getString(AffixPatternProvider.Flags.PADDING); 669 if (rawPaddingString.length() == 1) { 670 properties.setPadString(rawPaddingString); 671 } else if (rawPaddingString.length() == 2) { 672 if (rawPaddingString.charAt(0) == '\'') { 673 properties.setPadString("'"); 674 } else { 675 properties.setPadString(rawPaddingString); 676 } 677 } else { 678 properties.setPadString(rawPaddingString.substring(1, rawPaddingString.length() - 1)); 679 } 680 assert positive.paddingLocation != null; 681 properties.setPadPosition(positive.paddingLocation); 682 } else { 683 properties.setFormatWidth(-1); 684 properties.setPadString(null); 685 properties.setPadPosition(null); 686 } 687 688 // Set the affixes 689 // Always call the setter, even if the prefixes are empty, especially in the case of the 690 // negative prefix pattern, to prevent default values from overriding the pattern. 691 properties.setPositivePrefixPattern(posPrefix); 692 properties.setPositiveSuffixPattern(posSuffix); 693 if (patternInfo.negative != null) { 694 properties.setNegativePrefixPattern(patternInfo.getString( 695 AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN | AffixPatternProvider.Flags.PREFIX)); 696 properties.setNegativeSuffixPattern( 697 patternInfo.getString(AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN)); 698 } else { 699 properties.setNegativePrefixPattern(null); 700 properties.setNegativeSuffixPattern(null); 701 } 702 703 // Set the magnitude multiplier 704 if (positive.hasPercentSign) { 705 properties.setMagnitudeMultiplier(2); 706 } else if (positive.hasPerMilleSign) { 707 properties.setMagnitudeMultiplier(3); 708 } else { 709 properties.setMagnitudeMultiplier(0); 710 } 711 } 712 } 713