LEFT | RIGHT |
(no file at all) | |
1 // © 2017 and later: Unicode, Inc. and others. | 1 // © 2017 and later: Unicode, Inc. and others. |
2 // License & terms of use: http://www.unicode.org/copyright.html#License | 2 // License & terms of use: http://www.unicode.org/copyright.html#License |
3 package com.ibm.icu.impl.number; | 3 package com.ibm.icu.impl.number; |
4 | 4 |
5 import com.ibm.icu.impl.number.Padder.PadPosition; | 5 import com.ibm.icu.impl.number.Padder.PadPosition; |
6 | 6 |
7 /** Implements a recursive descent parser for decimal format patterns. */ | 7 /** Implements a recursive descent parser for decimal format patterns. */ |
8 public class PatternStringParser { | 8 public class PatternStringParser { |
9 | 9 |
10 public static final int IGNORE_ROUNDING_NEVER = 0; | 10 public static final int IGNORE_ROUNDING_NEVER = 0; |
11 public static final int IGNORE_ROUNDING_IF_CURRENCY = 1; | 11 public static final int IGNORE_ROUNDING_IF_CURRENCY = 1; |
12 public static final int IGNORE_ROUNDING_ALWAYS = 2; | 12 public static final int IGNORE_ROUNDING_ALWAYS = 2; |
13 | 13 |
14 /** | 14 /** |
15 * Runs the recursive descent parser on the given pattern string, returning
a data structure with raw information | 15 * Runs the recursive descent parser on the given pattern string, returning
a data structure with raw |
16 * about the pattern string. | 16 * information about the pattern string. |
17 * | 17 * |
18 * <p> | 18 * <p> |
19 * To obtain a more useful form of the data, consider using {@link #parseToP
roperties} instead. | 19 * To obtain a more useful form of the data, consider using {@link #parseToP
roperties} instead. |
20 * | 20 * |
21 * @param patternString | 21 * @param patternString |
22 * The LDML decimal format pattern (Excel-style pattern) to parse
. | 22 * The LDML decimal format pattern (Excel-style pattern) to parse
. |
23 * @return The results of the parse. | 23 * @return The results of the parse. |
24 */ | 24 */ |
25 public static ParsedPatternInfo parseToPatternInfo(String patternString) { | 25 public static ParsedPatternInfo parseToPatternInfo(String patternString) { |
26 ParserState state = new ParserState(patternString); | 26 ParserState state = new ParserState(patternString); |
27 ParsedPatternInfo result = new ParsedPatternInfo(patternString); | 27 ParsedPatternInfo result = new ParsedPatternInfo(patternString); |
28 consumePattern(state, result); | 28 consumePattern(state, result); |
29 return result; | 29 return result; |
30 } | 30 } |
31 | 31 |
32 /** | 32 /** |
33 * Parses a pattern string into a new property bag. | 33 * Parses a pattern string into a new property bag. |
34 * | 34 * |
35 * @param pattern | 35 * @param pattern |
36 * The pattern string, like "#,##0.00" | 36 * The pattern string, like "#,##0.00" |
37 * @param ignoreRounding | 37 * @param ignoreRounding |
38 * Whether to leave out rounding information (minFrac, maxFrac, a
nd rounding increment) when parsing the | 38 * Whether to leave out rounding information (minFrac, maxFrac, a
nd rounding increment) |
39 * pattern. This may be desirable if a custom rounding mode, such
as CurrencyUsage, is to be used | 39 * when parsing the pattern. This may be desirable if a custom ro
unding mode, such as |
40 * instead. One of {@link PatternStringParser#IGNORE_ROUNDING_ALW
AYS}, | 40 * CurrencyUsage, is to be used instead. One of |
| 41 * {@link PatternStringParser#IGNORE_ROUNDING_ALWAYS}, |
41 * {@link PatternStringParser#IGNORE_ROUNDING_IF_CURRENCY}, or | 42 * {@link PatternStringParser#IGNORE_ROUNDING_IF_CURRENCY}, or |
42 * {@link PatternStringParser#IGNORE_ROUNDING_NEVER}. | 43 * {@link PatternStringParser#IGNORE_ROUNDING_NEVER}. |
43 * @return A property bag object. | 44 * @return A property bag object. |
44 * @throws IllegalArgumentException | 45 * @throws IllegalArgumentException |
45 * If there is a syntax error in the pattern string. | 46 * If there is a syntax error in the pattern string. |
46 */ | 47 */ |
47 public static DecimalFormatProperties parseToProperties(String pattern, int
ignoreRounding) { | 48 public static DecimalFormatProperties parseToProperties(String pattern, int
ignoreRounding) { |
48 DecimalFormatProperties properties = new DecimalFormatProperties(); | 49 DecimalFormatProperties properties = new DecimalFormatProperties(); |
49 parseToExistingPropertiesImpl(pattern, properties, ignoreRounding); | 50 parseToExistingPropertiesImpl(pattern, properties, ignoreRounding); |
50 return properties; | 51 return properties; |
51 } | 52 } |
52 | 53 |
53 public static DecimalFormatProperties parseToProperties(String pattern) { | 54 public static DecimalFormatProperties parseToProperties(String pattern) { |
54 return parseToProperties(pattern, PatternStringParser.IGNORE_ROUNDING_NE
VER); | 55 return parseToProperties(pattern, PatternStringParser.IGNORE_ROUNDING_NE
VER); |
55 } | 56 } |
56 | 57 |
57 /** | 58 /** |
58 * Parses a pattern string into an existing property bag. All properties tha
t can be encoded into a pattern string | 59 * Parses a pattern string into an existing property bag. All properties tha
t can be encoded into a |
59 * will be overwritten with either their default value or with the value com
ing from the pattern string. Properties | 60 * pattern string will be overwritten with either their default value or wit
h the value coming from |
60 * that cannot be encoded into a pattern string, such as rounding mode, are
not modified. | 61 * the pattern string. Properties that cannot be encoded into a pattern stri
ng, such as rounding |
| 62 * mode, are not modified. |
61 * | 63 * |
62 * @param pattern | 64 * @param pattern |
63 * The pattern string, like "#,##0.00" | 65 * The pattern string, like "#,##0.00" |
64 * @param properties | 66 * @param properties |
65 * The property bag object to overwrite. | 67 * The property bag object to overwrite. |
66 * @param ignoreRounding | 68 * @param ignoreRounding |
67 * See {@link #parseToProperties(String pattern, int ignoreRoundi
ng)}. | 69 * See {@link #parseToProperties(String pattern, int ignoreRoundi
ng)}. |
68 * @throws IllegalArgumentException | 70 * @throws IllegalArgumentException |
69 * If there was a syntax error in the pattern string. | 71 * If there was a syntax error in the pattern string. |
70 */ | 72 */ |
71 public static void parseToExistingProperties(String pattern, DecimalFormatPr
operties properties, | 73 public static void parseToExistingProperties( |
| 74 String pattern, |
| 75 DecimalFormatProperties properties, |
72 int ignoreRounding) { | 76 int ignoreRounding) { |
73 parseToExistingPropertiesImpl(pattern, properties, ignoreRounding); | 77 parseToExistingPropertiesImpl(pattern, properties, ignoreRounding); |
74 } | 78 } |
75 | 79 |
76 public static void parseToExistingProperties(String pattern, DecimalFormatPr
operties properties) { | 80 public static void parseToExistingProperties(String pattern, DecimalFormatPr
operties properties) { |
77 parseToExistingProperties(pattern, properties, PatternStringParser.IGNOR
E_ROUNDING_NEVER); | 81 parseToExistingProperties(pattern, properties, PatternStringParser.IGNOR
E_ROUNDING_NEVER); |
78 } | 82 } |
79 | 83 |
80 /** | 84 /** |
81 * Contains raw information about the parsed decimal format pattern string. | 85 * Contains raw information about the parsed decimal format pattern string. |
(...skipping 22 matching lines...) Expand all Loading... |
104 public int length(int flags) { | 108 public int length(int flags) { |
105 return getLengthFromEndpoints(getEndpoints(flags)); | 109 return getLengthFromEndpoints(getEndpoints(flags)); |
106 } | 110 } |
107 | 111 |
108 public static int getLengthFromEndpoints(long endpoints) { | 112 public static int getLengthFromEndpoints(long endpoints) { |
109 int left = (int) (endpoints & 0xffffffff); | 113 int left = (int) (endpoints & 0xffffffff); |
110 int right = (int) (endpoints >>> 32); | 114 int right = (int) (endpoints >>> 32); |
111 return right - left; | 115 return right - left; |
112 } | 116 } |
113 | 117 |
| 118 @Override |
114 public String getString(int flags) { | 119 public String getString(int flags) { |
115 long endpoints = getEndpoints(flags); | 120 long endpoints = getEndpoints(flags); |
116 int left = (int) (endpoints & 0xffffffff); | 121 int left = (int) (endpoints & 0xffffffff); |
117 int right = (int) (endpoints >>> 32); | 122 int right = (int) (endpoints >>> 32); |
118 if (left == right) { | 123 if (left == right) { |
119 return ""; | 124 return ""; |
120 } | 125 } |
121 return pattern.substring(left, right); | 126 return pattern.substring(left, right); |
122 } | 127 } |
123 | 128 |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
255 consumePadding(state, result, PadPosition.BEFORE_PREFIX); | 260 consumePadding(state, result, PadPosition.BEFORE_PREFIX); |
256 result.prefixEndpoints = consumeAffix(state, result); | 261 result.prefixEndpoints = consumeAffix(state, result); |
257 consumePadding(state, result, PadPosition.AFTER_PREFIX); | 262 consumePadding(state, result, PadPosition.AFTER_PREFIX); |
258 consumeFormat(state, result); | 263 consumeFormat(state, result); |
259 consumeExponent(state, result); | 264 consumeExponent(state, result); |
260 consumePadding(state, result, PadPosition.BEFORE_SUFFIX); | 265 consumePadding(state, result, PadPosition.BEFORE_SUFFIX); |
261 result.suffixEndpoints = consumeAffix(state, result); | 266 result.suffixEndpoints = consumeAffix(state, result); |
262 consumePadding(state, result, PadPosition.AFTER_SUFFIX); | 267 consumePadding(state, result, PadPosition.AFTER_SUFFIX); |
263 } | 268 } |
264 | 269 |
265 private static void consumePadding(ParserState state, ParsedSubpatternInfo r
esult, PadPosition paddingLocation) { | 270 private static void consumePadding( |
| 271 ParserState state, |
| 272 ParsedSubpatternInfo result, |
| 273 PadPosition paddingLocation) { |
266 if (state.peek() != '*') { | 274 if (state.peek() != '*') { |
267 return; | 275 return; |
268 } | 276 } |
269 if (result.paddingLocation != null) { | 277 if (result.paddingLocation != null) { |
270 throw state.toParseException("Cannot have multiple pad specifiers"); | 278 throw state.toParseException("Cannot have multiple pad specifiers"); |
271 } | 279 } |
272 result.paddingLocation = paddingLocation; | 280 result.paddingLocation = paddingLocation; |
273 state.next(); // consume the '*' | 281 state.next(); // consume the '*' |
274 result.paddingEndpoints |= state.offset; | 282 result.paddingEndpoints |= state.offset; |
275 consumeLiteral(state); | 283 consumeLiteral(state); |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
497 state.next(); // consume the 0 | 505 state.next(); // consume the 0 |
498 result.exponentZeros += 1; | 506 result.exponentZeros += 1; |
499 result.widthExceptAffixes++; | 507 result.widthExceptAffixes++; |
500 } | 508 } |
501 } | 509 } |
502 | 510 |
503 /////////////////////////////////////////////////// | 511 /////////////////////////////////////////////////// |
504 /// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// | 512 /// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// |
505 /////////////////////////////////////////////////// | 513 /////////////////////////////////////////////////// |
506 | 514 |
507 private static void parseToExistingPropertiesImpl(String pattern, DecimalFor
matProperties properties, int ignoreRounding) { | 515 private static void parseToExistingPropertiesImpl( |
| 516 String pattern, |
| 517 DecimalFormatProperties properties, |
| 518 int ignoreRounding) { |
508 if (pattern == null || pattern.length() == 0) { | 519 if (pattern == null || pattern.length() == 0) { |
509 // Backwards compatibility requires that we reset to the default val
ues. | 520 // Backwards compatibility requires that we reset to the default val
ues. |
510 // TODO: Only overwrite the properties that "saveToProperties" norma
lly touches? | 521 // TODO: Only overwrite the properties that "saveToProperties" norma
lly touches? |
511 properties.clear(); | 522 properties.clear(); |
512 return; | 523 return; |
513 } | 524 } |
514 | 525 |
515 // TODO: Use thread locals here? | 526 // TODO: Use thread locals here? |
516 ParsedPatternInfo patternInfo = parseToPatternInfo(pattern); | 527 ParsedPatternInfo patternInfo = parseToPatternInfo(pattern); |
517 patternInfoToProperties(properties, patternInfo, ignoreRounding); | 528 patternInfoToProperties(properties, patternInfo, ignoreRounding); |
518 } | 529 } |
519 | 530 |
520 /** Finalizes the temporary data stored in the ParsedPatternInfo to the Prop
erties. */ | 531 /** Finalizes the temporary data stored in the ParsedPatternInfo to the Prop
erties. */ |
521 private static void patternInfoToProperties(DecimalFormatProperties properti
es, ParsedPatternInfo patternInfo, | 532 private static void patternInfoToProperties( |
| 533 DecimalFormatProperties properties, |
| 534 ParsedPatternInfo patternInfo, |
522 int _ignoreRounding) { | 535 int _ignoreRounding) { |
523 // Translate from PatternParseResult to Properties. | 536 // Translate from PatternParseResult to Properties. |
524 // Note that most data from "negative" is ignored per the specification
of DecimalFormat. | 537 // Note that most data from "negative" is ignored per the specification
of DecimalFormat. |
525 | 538 |
526 ParsedSubpatternInfo positive = patternInfo.positive; | 539 ParsedSubpatternInfo positive = patternInfo.positive; |
527 | 540 |
528 boolean ignoreRounding; | 541 boolean ignoreRounding; |
529 if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_NEVER) { | 542 if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_NEVER) { |
530 ignoreRounding = false; | 543 ignoreRounding = false; |
531 } else if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_IF_CUR
RENCY) { | 544 } else if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_IF_CUR
RENCY) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
565 minFrac = positive.fractionNumerals; | 578 minFrac = positive.fractionNumerals; |
566 } | 579 } |
567 | 580 |
568 // Rounding settings | 581 // Rounding settings |
569 // Don't set basic rounding when there is a currency sign; defer to Curr
encyUsage | 582 // Don't set basic rounding when there is a currency sign; defer to Curr
encyUsage |
570 if (positive.integerAtSigns > 0) { | 583 if (positive.integerAtSigns > 0) { |
571 properties.setMinimumFractionDigits(-1); | 584 properties.setMinimumFractionDigits(-1); |
572 properties.setMaximumFractionDigits(-1); | 585 properties.setMaximumFractionDigits(-1); |
573 properties.setRoundingIncrement(null); | 586 properties.setRoundingIncrement(null); |
574 properties.setMinimumSignificantDigits(positive.integerAtSigns); | 587 properties.setMinimumSignificantDigits(positive.integerAtSigns); |
575 properties.setMaximumSignificantDigits(positive.integerAtSigns + pos
itive.integerTrailingHashSigns); | 588 properties.setMaximumSignificantDigits( |
| 589 positive.integerAtSigns + positive.integerTrailingHashSigns)
; |
576 } else if (positive.rounding != null) { | 590 } else if (positive.rounding != null) { |
577 if (!ignoreRounding) { | 591 if (!ignoreRounding) { |
578 properties.setMinimumFractionDigits(minFrac); | 592 properties.setMinimumFractionDigits(minFrac); |
579 properties.setMaximumFractionDigits(positive.fractionTotal); | 593 properties.setMaximumFractionDigits(positive.fractionTotal); |
580 properties.setRoundingIncrement(positive.rounding.toBigDecimal()
.setScale(positive.fractionNumerals)); | 594 properties.setRoundingIncrement( |
| 595 positive.rounding.toBigDecimal().setScale(positive.fract
ionNumerals)); |
581 } else { | 596 } else { |
582 properties.setMinimumFractionDigits(-1); | 597 properties.setMinimumFractionDigits(-1); |
583 properties.setMaximumFractionDigits(-1); | 598 properties.setMaximumFractionDigits(-1); |
584 properties.setRoundingIncrement(null); | 599 properties.setRoundingIncrement(null); |
585 } | 600 } |
586 properties.setMinimumSignificantDigits(-1); | 601 properties.setMinimumSignificantDigits(-1); |
587 properties.setMaximumSignificantDigits(-1); | 602 properties.setMaximumSignificantDigits(-1); |
588 } else { | 603 } else { |
589 if (!ignoreRounding) { | 604 if (!ignoreRounding) { |
590 properties.setMinimumFractionDigits(minFrac); | 605 properties.setMinimumFractionDigits(minFrac); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
626 properties.setMaximumIntegerDigits(-1); | 641 properties.setMaximumIntegerDigits(-1); |
627 } | 642 } |
628 | 643 |
629 // Compute the affix patterns (required for both padding and affixes) | 644 // Compute the affix patterns (required for both padding and affixes) |
630 String posPrefix = patternInfo.getString(AffixPatternProvider.Flags.PREF
IX); | 645 String posPrefix = patternInfo.getString(AffixPatternProvider.Flags.PREF
IX); |
631 String posSuffix = patternInfo.getString(0); | 646 String posSuffix = patternInfo.getString(0); |
632 | 647 |
633 // Padding settings | 648 // Padding settings |
634 if (positive.paddingLocation != null) { | 649 if (positive.paddingLocation != null) { |
635 // The width of the positive prefix and suffix templates are include
d in the padding | 650 // The width of the positive prefix and suffix templates are include
d in the padding |
636 int paddingWidth = positive.widthExceptAffixes + AffixUtils.estimate
Length(posPrefix) | 651 int paddingWidth = positive.widthExceptAffixes |
| 652 + AffixUtils.estimateLength(posPrefix) |
637 + AffixUtils.estimateLength(posSuffix); | 653 + AffixUtils.estimateLength(posSuffix); |
638 properties.setFormatWidth(paddingWidth); | 654 properties.setFormatWidth(paddingWidth); |
639 String rawPaddingString = patternInfo.getString(AffixPatternProvider
.Flags.PADDING); | 655 String rawPaddingString = patternInfo.getString(AffixPatternProvider
.Flags.PADDING); |
640 if (rawPaddingString.length() == 1) { | 656 if (rawPaddingString.length() == 1) { |
641 properties.setPadString(rawPaddingString); | 657 properties.setPadString(rawPaddingString); |
642 } else if (rawPaddingString.length() == 2) { | 658 } else if (rawPaddingString.length() == 2) { |
643 if (rawPaddingString.charAt(0) == '\'') { | 659 if (rawPaddingString.charAt(0) == '\'') { |
644 properties.setPadString("'"); | 660 properties.setPadString("'"); |
645 } else { | 661 } else { |
646 properties.setPadString(rawPaddingString); | 662 properties.setPadString(rawPaddingString); |
647 } | 663 } |
648 } else { | 664 } else { |
649 properties.setPadString(rawPaddingString.substring(1, rawPadding
String.length() - 1)); | 665 properties.setPadString(rawPaddingString.substring(1, rawPadding
String.length() - 1)); |
650 } | 666 } |
651 assert positive.paddingLocation != null; | 667 assert positive.paddingLocation != null; |
652 properties.setPadPosition(positive.paddingLocation); | 668 properties.setPadPosition(positive.paddingLocation); |
653 } else { | 669 } else { |
654 properties.setFormatWidth(-1); | 670 properties.setFormatWidth(-1); |
655 properties.setPadString(null); | 671 properties.setPadString(null); |
656 properties.setPadPosition(null); | 672 properties.setPadPosition(null); |
657 } | 673 } |
658 | 674 |
659 // Set the affixes | 675 // Set the affixes |
660 // Always call the setter, even if the prefixes are empty, especially in
the case of the | 676 // Always call the setter, even if the prefixes are empty, especially in
the case of the |
661 // negative prefix pattern, to prevent default values from overriding th
e pattern. | 677 // negative prefix pattern, to prevent default values from overriding th
e pattern. |
662 properties.setPositivePrefixPattern(posPrefix); | 678 properties.setPositivePrefixPattern(posPrefix); |
663 properties.setPositiveSuffixPattern(posSuffix); | 679 properties.setPositiveSuffixPattern(posSuffix); |
664 if (patternInfo.negative != null) { | 680 if (patternInfo.negative != null) { |
665 properties.setNegativePrefixPattern(patternInfo | 681 properties.setNegativePrefixPattern(patternInfo.getString( |
666 .getString(AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN |
AffixPatternProvider.Flags.PREFIX)); | 682 AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN | AffixPatter
nProvider.Flags.PREFIX)); |
667 properties.setNegativeSuffixPattern(patternInfo.getString(AffixPatte
rnProvider.Flags.NEGATIVE_SUBPATTERN)); | 683 properties.setNegativeSuffixPattern( |
| 684 patternInfo.getString(AffixPatternProvider.Flags.NEGATIVE_SU
BPATTERN)); |
668 } else { | 685 } else { |
669 properties.setNegativePrefixPattern(null); | 686 properties.setNegativePrefixPattern(null); |
670 properties.setNegativeSuffixPattern(null); | 687 properties.setNegativeSuffixPattern(null); |
671 } | 688 } |
672 | 689 |
673 // Set the magnitude multiplier | 690 // Set the magnitude multiplier |
674 if (positive.hasPercentSign) { | 691 if (positive.hasPercentSign) { |
675 properties.setMagnitudeMultiplier(2); | 692 properties.setMagnitudeMultiplier(2); |
676 } else if (positive.hasPerMilleSign) { | 693 } else if (positive.hasPerMilleSign) { |
677 properties.setMagnitudeMultiplier(3); | 694 properties.setMagnitudeMultiplier(3); |
678 } else { | 695 } else { |
679 properties.setMagnitudeMultiplier(0); | 696 properties.setMagnitudeMultiplier(0); |
680 } | 697 } |
681 } | 698 } |
682 } | 699 } |
LEFT | RIGHT |