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