LEFT | RIGHT |
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 java.math.BigDecimal; | 5 import java.math.BigDecimal; |
6 | 6 |
7 import com.ibm.icu.impl.number.formatters.PaddingFormat; | 7 import com.ibm.icu.impl.number.formatters.PaddingFormat; |
8 import com.ibm.icu.impl.number.formatters.PaddingFormat.PaddingLocation; | 8 import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; |
9 import com.ibm.icu.text.DecimalFormatSymbols; | 9 import com.ibm.icu.text.DecimalFormatSymbols; |
10 | 10 |
11 /** | 11 /** |
12 * Handles parsing and creation of the compact pattern string representation of
a decimal format. | 12 * Handles parsing and creation of the compact pattern string representation of
a decimal format. |
13 */ | 13 */ |
14 public class PatternString { | 14 public class PatternString { |
15 | 15 |
16 /** | 16 /** |
17 * Parses a pattern string into a new property bag. | 17 * Parses a pattern string into a new property bag. |
18 * | 18 * |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
66 * @return A pattern string approximately serializing the property bag. | 66 * @return A pattern string approximately serializing the property bag. |
67 */ | 67 */ |
68 public static String propertiesToString(Properties properties) { | 68 public static String propertiesToString(Properties properties) { |
69 StringBuilder sb = new StringBuilder(); | 69 StringBuilder sb = new StringBuilder(); |
70 | 70 |
71 // Convenience references | 71 // Convenience references |
72 // The Math.min() calls prevent DoS | 72 // The Math.min() calls prevent DoS |
73 int dosMax = 100; | 73 int dosMax = 100; |
74 int groupingSize = Math.min(properties.getSecondaryGroupingSize(), dosMax); | 74 int groupingSize = Math.min(properties.getSecondaryGroupingSize(), dosMax); |
75 int firstGroupingSize = Math.min(properties.getGroupingSize(), dosMax); | 75 int firstGroupingSize = Math.min(properties.getGroupingSize(), dosMax); |
76 int paddingWidth = Math.min(properties.getPaddingWidth(), dosMax); | 76 int paddingWidth = Math.min(properties.getFormatWidth(), dosMax); |
77 PaddingLocation paddingLocation = properties.getPaddingLocation(); | 77 PadPosition paddingLocation = properties.getPadPosition(); |
78 CharSequence paddingString = properties.getPaddingString(); | 78 String paddingString = properties.getPadString(); |
79 int minInt = Math.max(Math.min(properties.getMinimumIntegerDigits(), dosMax)
, 0); | 79 int minInt = Math.max(Math.min(properties.getMinimumIntegerDigits(), dosMax)
, 0); |
80 int maxInt = Math.min(properties.getMaximumIntegerDigits(), dosMax); | 80 int maxInt = Math.min(properties.getMaximumIntegerDigits(), dosMax); |
81 int minFrac = Math.max(Math.min(properties.getMinimumFractionDigits(), dosMa
x), 0); | 81 int minFrac = Math.max(Math.min(properties.getMinimumFractionDigits(), dosMa
x), 0); |
82 int maxFrac = Math.min(properties.getMaximumFractionDigits(), dosMax); | 82 int maxFrac = Math.min(properties.getMaximumFractionDigits(), dosMax); |
83 int minSig = Math.min(properties.getMinimumSignificantDigits(), dosMax); | 83 int minSig = Math.min(properties.getMinimumSignificantDigits(), dosMax); |
84 int maxSig = Math.min(properties.getMaximumSignificantDigits(), dosMax); | 84 int maxSig = Math.min(properties.getMaximumSignificantDigits(), dosMax); |
85 boolean alwaysShowDecimal = properties.getAlwaysShowDecimal(); | 85 boolean alwaysShowDecimal = properties.getDecimalSeparatorAlwaysShown(); |
86 int exponentDigits = Math.min(properties.getExponentDigits(), dosMax); | 86 int exponentDigits = Math.min(properties.getMinimumExponentDigits(), dosMax)
; |
87 boolean exponentShowPlusSign = properties.getExponentShowPlusSign(); | 87 boolean exponentShowPlusSign = properties.getExponentSignAlwaysShown(); |
88 CharSequence pp = properties.getPositivePrefix(); | 88 String pp = properties.getPositivePrefix(); |
89 CharSequence ppp = properties.getPositivePrefixPattern(); | 89 String ppp = properties.getPositivePrefixPattern(); |
90 CharSequence ps = properties.getPositiveSuffix(); | 90 String ps = properties.getPositiveSuffix(); |
91 CharSequence psp = properties.getPositiveSuffixPattern(); | 91 String psp = properties.getPositiveSuffixPattern(); |
92 CharSequence np = properties.getNegativePrefix(); | 92 String np = properties.getNegativePrefix(); |
93 CharSequence npp = properties.getNegativePrefixPattern(); | 93 String npp = properties.getNegativePrefixPattern(); |
94 CharSequence ns = properties.getNegativeSuffix(); | 94 String ns = properties.getNegativeSuffix(); |
95 CharSequence nsp = properties.getNegativeSuffixPattern(); | 95 String nsp = properties.getNegativeSuffixPattern(); |
96 | 96 |
97 // Prefixes | 97 // Prefixes |
98 if (ppp != null) sb.append(ppp); | 98 if (ppp != null) sb.append(ppp); |
99 AffixPatternUtils.escape(pp, sb); | 99 AffixPatternUtils.escape(pp, sb); |
100 int afterPrefixPos = sb.length(); | 100 int afterPrefixPos = sb.length(); |
101 | 101 |
102 // Figure out the grouping sizes. | 102 // Figure out the grouping sizes. |
103 int grouping1, grouping2, grouping; | 103 int grouping1, grouping2, grouping; |
104 if (groupingSize != Math.min(dosMax, Properties.DEFAULT_SECONDARY_GROUPING_S
IZE) | 104 if (groupingSize != Math.min(dosMax, Properties.DEFAULT_SECONDARY_GROUPING_S
IZE) |
105 && firstGroupingSize != Math.min(dosMax, Properties.DEFAULT_GROUPING_SIZ
E) | 105 && firstGroupingSize != Math.min(dosMax, Properties.DEFAULT_GROUPING_SIZ
E) |
(...skipping 10 matching lines...) Expand all Loading... |
116 grouping1 = 0; | 116 grouping1 = 0; |
117 grouping2 = firstGroupingSize; | 117 grouping2 = firstGroupingSize; |
118 } else { | 118 } else { |
119 grouping = 0; | 119 grouping = 0; |
120 grouping1 = 0; | 120 grouping1 = 0; |
121 grouping2 = 0; | 121 grouping2 = 0; |
122 } | 122 } |
123 int groupingLength = grouping1 + grouping2 + 1; | 123 int groupingLength = grouping1 + grouping2 + 1; |
124 | 124 |
125 // Figure out the digits we need to put in the pattern. | 125 // Figure out the digits we need to put in the pattern. |
126 BigDecimal roundingInterval = properties.getRoundingInterval(); | 126 BigDecimal roundingInterval = properties.getRoundingIncrement(); |
127 StringBuilder digitsString = new StringBuilder(); | 127 StringBuilder digitsString = new StringBuilder(); |
128 int digitsStringScale = 0; | 128 int digitsStringScale = 0; |
129 if (maxSig != Math.min(dosMax, Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS
)) { | 129 if (maxSig != Math.min(dosMax, Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS
)) { |
130 // Significant Digits. | 130 // Significant Digits. |
131 while (digitsString.length() < minSig) { | 131 while (digitsString.length() < minSig) { |
132 digitsString.append('@'); | 132 digitsString.append('@'); |
133 } | 133 } |
134 while (digitsString.length() < maxSig) { | 134 while (digitsString.length() < maxSig) { |
135 digitsString.append('#'); | 135 digitsString.append('#'); |
136 } | 136 } |
137 } else if (roundingInterval != Properties.DEFAULT_ROUNDING_INTERVAL) { | 137 } else if (roundingInterval != Properties.DEFAULT_ROUNDING_INCREMENT) { |
138 // Rounding Interval. | 138 // Rounding Interval. |
139 digitsStringScale = -roundingInterval.scale(); | 139 digitsStringScale = -roundingInterval.scale(); |
140 // TODO: Check for DoS here? | 140 // TODO: Check for DoS here? |
141 String str = roundingInterval.scaleByPowerOfTen(roundingInterval.scale()).
toPlainString(); | 141 String str = roundingInterval.scaleByPowerOfTen(roundingInterval.scale()).
toPlainString(); |
142 if (str.charAt(0) == '\'') { | 142 if (str.charAt(0) == '\'') { |
143 // TODO: Unsupported operation exception or fail silently? | 143 // TODO: Unsupported operation exception or fail silently? |
144 digitsString.append(str, 1, str.length()); | 144 digitsString.append(str, 1, str.length()); |
145 } else { | 145 } else { |
146 digitsString.append(str); | 146 digitsString.append(str); |
147 } | 147 } |
(...skipping 20 matching lines...) Expand all Loading... |
168 if (magnitude > grouping2 && grouping > 0 && (magnitude - grouping2) % gro
uping == 0) { | 168 if (magnitude > grouping2 && grouping > 0 && (magnitude - grouping2) % gro
uping == 0) { |
169 sb.append(','); | 169 sb.append(','); |
170 } else if (magnitude > 0 && magnitude == grouping2) { | 170 } else if (magnitude > 0 && magnitude == grouping2) { |
171 sb.append(','); | 171 sb.append(','); |
172 } else if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) { | 172 } else if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) { |
173 sb.append('.'); | 173 sb.append('.'); |
174 } | 174 } |
175 } | 175 } |
176 | 176 |
177 // Exponential notation | 177 // Exponential notation |
178 if (exponentDigits != Math.min(dosMax, Properties.DEFAULT_EXPONENT_DIGITS))
{ | 178 if (exponentDigits != Math.min(dosMax, Properties.DEFAULT_MINIMUM_EXPONENT_D
IGITS)) { |
179 sb.append('E'); | 179 sb.append('E'); |
180 if (exponentShowPlusSign) { | 180 if (exponentShowPlusSign) { |
181 sb.append('+'); | 181 sb.append('+'); |
182 } | 182 } |
183 for (int i = 0; i < exponentDigits; i++) { | 183 for (int i = 0; i < exponentDigits; i++) { |
184 sb.append('0'); | 184 sb.append('0'); |
185 } | 185 } |
186 } | 186 } |
187 | 187 |
188 // Suffixes | 188 // Suffixes |
189 int beforeSuffixPos = sb.length(); | 189 int beforeSuffixPos = sb.length(); |
190 if (psp != null) sb.append(psp); | 190 if (psp != null) sb.append(psp); |
191 AffixPatternUtils.escape(ps, sb); | 191 AffixPatternUtils.escape(ps, sb); |
192 | 192 |
193 // Resolve Padding | 193 // Resolve Padding |
194 if (paddingWidth != Properties.DEFAULT_PADDING_WIDTH) { | 194 if (paddingWidth != Properties.DEFAULT_FORMAT_WIDTH) { |
195 while (paddingWidth - sb.length() > 0) { | 195 while (paddingWidth - sb.length() > 0) { |
196 sb.insert(afterPrefixPos, '#'); | 196 sb.insert(afterPrefixPos, '#'); |
197 beforeSuffixPos++; | 197 beforeSuffixPos++; |
198 } | 198 } |
199 int addedLength; | 199 int addedLength; |
200 switch (paddingLocation) { | 200 switch (paddingLocation) { |
201 case BEFORE_PREFIX: | 201 case BEFORE_PREFIX: |
202 addedLength = escapePaddingString(paddingString, sb, 0); | 202 addedLength = escapePaddingString(paddingString, sb, 0); |
203 sb.insert(0, '*'); | 203 sb.insert(0, '*'); |
204 afterPrefixPos += addedLength + 1; | 204 afterPrefixPos += addedLength + 1; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
238 } | 238 } |
239 | 239 |
240 return sb.toString(); | 240 return sb.toString(); |
241 } | 241 } |
242 | 242 |
243 /** @return The number of chars inserted. */ | 243 /** @return The number of chars inserted. */ |
244 private static int escapePaddingString(CharSequence input, StringBuilder outpu
t, int startIndex) { | 244 private static int escapePaddingString(CharSequence input, StringBuilder outpu
t, int startIndex) { |
245 if (input == null || input.length() == 0) input = PaddingFormat.FALLBACK_PAD
DING_STRING; | 245 if (input == null || input.length() == 0) input = PaddingFormat.FALLBACK_PAD
DING_STRING; |
246 int startLength = output.length(); | 246 int startLength = output.length(); |
247 if (input.length() == 1) { | 247 if (input.length() == 1) { |
248 output.insert(startIndex, input); | 248 if (input.equals("'")) { |
| 249 output.insert(startIndex, "''"); |
| 250 } else { |
| 251 output.insert(startIndex, input); |
| 252 } |
249 } else { | 253 } else { |
250 output.insert(startIndex, '\''); | 254 output.insert(startIndex, '\''); |
| 255 int offset = 1; |
251 for (int i = 0; i < input.length(); i++) { | 256 for (int i = 0; i < input.length(); i++) { |
252 // it's okay to deal in chars here because the quote mark is the only in
teresting thing. | 257 // it's okay to deal in chars here because the quote mark is the only in
teresting thing. |
253 char ch = input.charAt(i); | 258 char ch = input.charAt(i); |
254 if (ch == '\'') { | 259 if (ch == '\'') { |
255 output.insert(startIndex, "''"); | 260 output.insert(startIndex + offset, "''"); |
256 } else { | 261 offset += 2; |
257 output.insert(startIndex, ch); | 262 } else { |
258 } | 263 output.insert(startIndex + offset, ch); |
259 } | 264 offset += 1; |
260 output.insert(startIndex, '\''); | 265 } |
| 266 } |
| 267 output.insert(startIndex + offset, '\''); |
261 } | 268 } |
262 return output.length() - startLength; | 269 return output.length() - startLength; |
263 } | 270 } |
264 | 271 |
265 /** | 272 /** |
266 * Converts a pattern between standard notation and localized notation. Locali
zed notation means | 273 * Converts a pattern between standard notation and localized notation. Locali
zed notation means |
267 * that instead of using generic placeholders in the pattern, you use the corr
esponding | 274 * that instead of using generic placeholders in the pattern, you use the corr
esponding |
268 * locale-specific characters instead. | 275 * locale-specific characters instead. For example, in locale <em>fr-FR</em>,
the period in the |
| 276 * pattern "0.000" means "decimal" in standard notation (as it does in every o
ther locale), but it |
| 277 * means "grouping" in localized notation. |
269 * | 278 * |
270 * @param input The pattern to convert. | 279 * @param input The pattern to convert. |
271 * @param symbols The symbols corresponding to the localized pattern. | 280 * @param symbols The symbols corresponding to the localized pattern. |
272 * @param toLocalized true to convert from standard to localized notation; fal
se to convert from | 281 * @param toLocalized true to convert from standard to localized notation; fal
se to convert from |
273 * localized to standard notation. | 282 * localized to standard notation. |
274 * @return The pattern expressed in the other notation. | 283 * @return The pattern expressed in the other notation. |
275 * @deprecated ICU 59 This method is provided for backwards compatibility and
should not be used | 284 * @deprecated ICU 59 This method is provided for backwards compatibility and
should not be used |
276 * in any new code. | 285 * in any new code. |
277 */ | 286 */ |
278 @Deprecated | 287 @Deprecated |
279 public static String convertLocalized( | 288 public static String convertLocalized( |
280 CharSequence input, DecimalFormatSymbols symbols, boolean toLocalized) { | 289 CharSequence input, DecimalFormatSymbols symbols, boolean toLocalized) { |
281 if (input == null) return null; | 290 if (input == null) return null; |
282 | 291 |
| 292 /// This is not the prettiest function in the world, but it gets the job don
e. /// |
| 293 |
283 // Construct a table of code points to be converted between localized and st
andard. | 294 // Construct a table of code points to be converted between localized and st
andard. |
284 int[][] table = new int[6][2]; | 295 int[][] table = new int[6][2]; |
285 int standIdx = toLocalized ? 0 : 1; | 296 int standIdx = toLocalized ? 0 : 1; |
286 int localIdx = toLocalized ? 1 : 0; | 297 int localIdx = toLocalized ? 1 : 0; |
287 table[0][standIdx] = '%'; | 298 table[0][standIdx] = '%'; |
288 table[0][localIdx] = Character.codePointAt(symbols.getPercentString(), 0); | 299 table[0][localIdx] = symbols.getPercent(); |
289 table[1][standIdx] = '‰'; | 300 table[1][standIdx] = '‰'; |
290 table[1][localIdx] = Character.codePointAt(symbols.getPerMillString(), 0); | 301 table[1][localIdx] = symbols.getPerMill(); |
291 table[2][standIdx] = '.'; | 302 table[2][standIdx] = '.'; |
292 table[2][localIdx] = Character.codePointAt(symbols.getDecimalSeparatorString
(), 0); | 303 table[2][localIdx] = symbols.getDecimalSeparator(); |
293 table[3][standIdx] = ','; | 304 table[3][standIdx] = ','; |
294 table[3][localIdx] = Character.codePointAt(symbols.getGroupingSeparatorStrin
g(), 0); | 305 table[3][localIdx] = symbols.getGroupingSeparator(); |
295 table[4][standIdx] = '-'; | 306 table[4][standIdx] = '-'; |
296 table[4][localIdx] = Character.codePointAt(symbols.getMinusSignString(), 0); | 307 table[4][localIdx] = symbols.getMinusSign(); |
297 table[5][standIdx] = '+'; | 308 table[5][standIdx] = '+'; |
298 table[5][localIdx] = Character.codePointAt(symbols.getPlusSignString(), 0); | 309 table[5][localIdx] = symbols.getPlusSign(); |
| 310 |
| 311 // Special case: localIdx characters are NOT allowed to be quotes, like in d
e_CH. |
| 312 // Use '’' instead. |
| 313 for (int i = 0; i < table.length; i++) { |
| 314 if (table[i][localIdx] == '\'') { |
| 315 table[i][localIdx] = '’'; |
| 316 } |
| 317 } |
299 | 318 |
300 // Iterate through the string and convert | 319 // Iterate through the string and convert |
301 int offset = 0; | 320 int offset = 0; |
302 boolean insideQuote = false; | 321 int state = 0; |
303 StringBuilder result = new StringBuilder(); | 322 StringBuilder result = new StringBuilder(); |
304 for (; offset < input.length(); ) { | 323 for (; offset < input.length(); ) { |
305 int cp = Character.codePointAt(input, offset); | 324 int cp = Character.codePointAt(input, offset); |
306 int cpToAppend = cp; | 325 int cpToAppend = cp; |
307 if (insideQuote) { | 326 |
| 327 if (state == 1 || state == 3 || state == 4) { |
| 328 // Inside user-specified quote |
308 if (cp == '\'') { | 329 if (cp == '\'') { |
309 insideQuote = false; | 330 if (state == 1) { |
| 331 state = 0; |
| 332 } else if (state == 3) { |
| 333 state = 2; |
| 334 cpToAppend = -1; |
| 335 } else { |
| 336 state = 2; |
| 337 } |
310 } | 338 } |
311 } else { | 339 } else { |
| 340 // Base state or inside special character quote |
312 if (cp == '\'') { | 341 if (cp == '\'') { |
313 insideQuote = true; | 342 if (state == 2 && offset + 1 < input.length()) { |
314 } else { | 343 int nextCp = Character.codePointAt(input, offset + 1); |
| 344 if (nextCp == '\'') { |
| 345 // escaped quote |
| 346 state = 4; |
| 347 } else { |
| 348 // begin user-specified quote sequence |
| 349 // we are already in a quote sequence, so omit the opening quote |
| 350 state = 3; |
| 351 cpToAppend = -1; |
| 352 } |
| 353 } else { |
| 354 state = 1; |
| 355 } |
| 356 } else { |
| 357 boolean needsSpecialQuote = false; |
315 for (int i = 0; i < table.length; i++) { | 358 for (int i = 0; i < table.length; i++) { |
316 if (table[i][0] == cp) { | 359 if (table[i][0] == cp) { |
317 cpToAppend = table[i][1]; | 360 cpToAppend = table[i][1]; |
| 361 needsSpecialQuote = false; // in case an earlier translation trigg
ered it |
318 break; | 362 break; |
| 363 } else if (table[i][1] == cp) { |
| 364 needsSpecialQuote = true; |
319 } | 365 } |
320 } | 366 } |
321 } | 367 if (state == 0 && needsSpecialQuote) { |
322 } | 368 state = 2; |
323 result.appendCodePoint(cpToAppend); | 369 result.appendCodePoint('\''); |
| 370 } else if (state == 2 && !needsSpecialQuote) { |
| 371 state = 0; |
| 372 result.appendCodePoint('\''); |
| 373 } |
| 374 } |
| 375 } |
| 376 if (cpToAppend != -1) { |
| 377 result.appendCodePoint(cpToAppend); |
| 378 } |
324 offset += Character.charCount(cp); | 379 offset += Character.charCount(cp); |
| 380 } |
| 381 if (state == 2) { |
| 382 result.appendCodePoint('\''); |
325 } | 383 } |
326 return result.toString(); | 384 return result.toString(); |
327 } | 385 } |
328 | 386 |
329 /** Implements a recursive descent parser for decimal format patterns. */ | 387 /** Implements a recursive descent parser for decimal format patterns. */ |
330 static class LdmlDecimalPatternParser { | 388 static class LdmlDecimalPatternParser { |
331 | 389 |
332 /** | 390 /** |
333 * An internal, intermediate data structure used for storing parse results b
efore they are | 391 * An internal, intermediate data structure used for storing parse results b
efore they are |
334 * finalized into a DecimalFormatPattern.Builder. | 392 * finalized into a DecimalFormatPattern.Builder. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
367 } else { | 425 } else { |
368 minInt = positive.minimumIntegerDigits; | 426 minInt = positive.minimumIntegerDigits; |
369 minFrac = positive.minimumFractionDigits; | 427 minFrac = positive.minimumFractionDigits; |
370 } | 428 } |
371 | 429 |
372 // Rounding settings | 430 // Rounding settings |
373 // Don't set basic rounding when there is a currency sign; defer to Curr
encyUsage | 431 // Don't set basic rounding when there is a currency sign; defer to Curr
encyUsage |
374 if (positive.minimumSignificantDigits > 0) { | 432 if (positive.minimumSignificantDigits > 0) { |
375 properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACTIO
N_DIGITS); | 433 properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACTIO
N_DIGITS); |
376 properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACTIO
N_DIGITS); | 434 properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACTIO
N_DIGITS); |
377 properties.setRoundingInterval(Properties.DEFAULT_ROUNDING_INTERVAL); | 435 properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMENT)
; |
378 properties.setMinimumSignificantDigits(positive.minimumSignificantDigi
ts); | 436 properties.setMinimumSignificantDigits(positive.minimumSignificantDigi
ts); |
379 properties.setMaximumSignificantDigits(positive.maximumSignificantDigi
ts); | 437 properties.setMaximumSignificantDigits(positive.maximumSignificantDigi
ts); |
380 } else if (!positive.rounding.isZero()) { | 438 } else if (!positive.rounding.isZero()) { |
381 if (!ignoreRounding) { | 439 if (!ignoreRounding) { |
382 properties.setMinimumFractionDigits(minFrac); | 440 properties.setMinimumFractionDigits(minFrac); |
383 properties.setMaximumFractionDigits(positive.maximumFractionDigits); | 441 properties.setMaximumFractionDigits(positive.maximumFractionDigits); |
384 properties.setRoundingInterval(positive.rounding.toBigDecimal()); | 442 properties.setRoundingIncrement(positive.rounding.toBigDecimal()); |
385 } else { | 443 } else { |
386 properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACT
ION_DIGITS); | 444 properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACT
ION_DIGITS); |
387 properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACT
ION_DIGITS); | 445 properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACT
ION_DIGITS); |
388 properties.setRoundingInterval(Properties.DEFAULT_ROUNDING_INTERVAL)
; | 446 properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMEN
T); |
389 } | 447 } |
390 properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGN
IFICANT_DIGITS); | 448 properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGN
IFICANT_DIGITS); |
391 properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGN
IFICANT_DIGITS); | 449 properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGN
IFICANT_DIGITS); |
392 } else { | 450 } else { |
393 if (!ignoreRounding) { | 451 if (!ignoreRounding) { |
394 properties.setMinimumFractionDigits(minFrac); | 452 properties.setMinimumFractionDigits(minFrac); |
395 properties.setMaximumFractionDigits(positive.maximumFractionDigits); | 453 properties.setMaximumFractionDigits(positive.maximumFractionDigits); |
396 properties.setRoundingInterval(Properties.DEFAULT_ROUNDING_INTERVAL)
; | 454 properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMEN
T); |
397 } else { | 455 } else { |
398 properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACT
ION_DIGITS); | 456 properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACT
ION_DIGITS); |
399 properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACT
ION_DIGITS); | 457 properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACT
ION_DIGITS); |
400 properties.setRoundingInterval(Properties.DEFAULT_ROUNDING_INTERVAL)
; | 458 properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMEN
T); |
401 } | 459 } |
402 properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGN
IFICANT_DIGITS); | 460 properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGN
IFICANT_DIGITS); |
403 properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGN
IFICANT_DIGITS); | 461 properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGN
IFICANT_DIGITS); |
404 } | 462 } |
405 | 463 |
406 // If the pattern ends with a '.' then force the decimal point. | 464 // If the pattern ends with a '.' then force the decimal point. |
407 if (positive.hasDecimal && positive.maximumFractionDigits == 0) { | 465 if (positive.hasDecimal && positive.maximumFractionDigits == 0) { |
408 properties.setAlwaysShowDecimal(true); | 466 properties.setDecimalSeparatorAlwaysShown(true); |
409 } else { | 467 } else { |
410 properties.setAlwaysShowDecimal(false); | 468 properties.setDecimalSeparatorAlwaysShown(false); |
411 } | 469 } |
412 | 470 |
413 // Scientific notation settings | 471 // Scientific notation settings |
414 if (positive.exponentDigits > 0) { | 472 if (positive.exponentDigits > 0) { |
415 properties.setExponentShowPlusSign(positive.exponentShowPlusSign); | 473 properties.setExponentSignAlwaysShown(positive.exponentShowPlusSign); |
416 properties.setExponentDigits(positive.exponentDigits); | 474 properties.setMinimumExponentDigits(positive.exponentDigits); |
417 if (positive.minimumSignificantDigits == 0) { | 475 if (positive.minimumSignificantDigits == 0) { |
418 // patterns without '@' can define max integer digits, used for engi
neering notation | 476 // patterns without '@' can define max integer digits, used for engi
neering notation |
419 properties.setMinimumIntegerDigits(positive.minimumIntegerDigits); | 477 properties.setMinimumIntegerDigits(positive.minimumIntegerDigits); |
420 properties.setMaximumIntegerDigits(positive.totalIntegerDigits); | 478 properties.setMaximumIntegerDigits(positive.totalIntegerDigits); |
421 } else { | 479 } else { |
422 // patterns with '@' cannot define max integer digits | 480 // patterns with '@' cannot define max integer digits |
423 properties.setMinimumIntegerDigits(1); | 481 properties.setMinimumIntegerDigits(1); |
424 properties.setMaximumIntegerDigits(Properties.DEFAULT_MAXIMUM_INTEGE
R_DIGITS); | 482 properties.setMaximumIntegerDigits(Properties.DEFAULT_MAXIMUM_INTEGE
R_DIGITS); |
425 } | 483 } |
426 } else { | 484 } else { |
427 properties.setExponentShowPlusSign(Properties.DEFAULT_EXPONENT_SHOW_PL
US_SIGN); | 485 properties.setExponentSignAlwaysShown(Properties.DEFAULT_EXPONENT_SIGN
_ALWAYS_SHOWN); |
428 properties.setExponentDigits(Properties.DEFAULT_EXPONENT_DIGITS); | 486 properties.setMinimumExponentDigits(Properties.DEFAULT_MINIMUM_EXPONEN
T_DIGITS); |
429 properties.setMinimumIntegerDigits(minInt); | 487 properties.setMinimumIntegerDigits(minInt); |
430 properties.setMaximumIntegerDigits(Properties.DEFAULT_MAXIMUM_INTEGER_
DIGITS); | 488 properties.setMaximumIntegerDigits(Properties.DEFAULT_MAXIMUM_INTEGER_
DIGITS); |
431 } | 489 } |
432 | 490 |
433 // Padding settings | 491 // Padding settings |
434 if (positive.padding.length() > 0) { | 492 if (positive.padding.length() > 0) { |
435 // The width of the positive prefix and suffix templates are included
in the padding | 493 // The width of the positive prefix and suffix templates are included
in the padding |
436 int paddingWidth = | 494 int paddingWidth = |
437 positive.paddingWidth | 495 positive.paddingWidth |
438 + AffixPatternUtils.unescapedLength(positive.prefix) | 496 + AffixPatternUtils.unescapedLength(positive.prefix) |
439 + AffixPatternUtils.unescapedLength(positive.suffix); | 497 + AffixPatternUtils.unescapedLength(positive.suffix); |
440 properties.setPaddingWidth(paddingWidth); | 498 properties.setFormatWidth(paddingWidth); |
441 if (positive.padding.length() > 1) { | 499 if (positive.padding.length() == 1) { |
442 // TODO: What if the custom padding string has a quote character? | 500 properties.setPadString(positive.padding.toString()); |
443 properties.setPaddingString( | 501 } else if (positive.padding.length() == 2) { |
| 502 if (positive.padding.charAt(0) == '\'') { |
| 503 properties.setPadString("'"); |
| 504 } else { |
| 505 properties.setPadString(positive.padding.toString()); |
| 506 } |
| 507 } else { |
| 508 properties.setPadString( |
444 positive.padding.subSequence(1, positive.padding.length() - 1).t
oString()); | 509 positive.padding.subSequence(1, positive.padding.length() - 1).t
oString()); |
445 } else { | |
446 properties.setPaddingString(positive.padding.toString()); | |
447 } | 510 } |
448 assert positive.paddingLocation != null; | 511 assert positive.paddingLocation != null; |
449 properties.setPaddingLocation(positive.paddingLocation); | 512 properties.setPadPosition(positive.paddingLocation); |
450 } else { | 513 } else { |
451 properties.setPaddingWidth(Properties.DEFAULT_PADDING_WIDTH); | 514 properties.setFormatWidth(Properties.DEFAULT_FORMAT_WIDTH); |
452 properties.setPaddingString(Properties.DEFAULT_PADDING_STRING); | 515 properties.setPadString(Properties.DEFAULT_PAD_STRING); |
453 properties.setPaddingLocation(Properties.DEFAULT_PADDING_LOCATION); | 516 properties.setPadPosition(Properties.DEFAULT_PAD_POSITION); |
454 } | 517 } |
455 | 518 |
456 // Set the affixes | 519 // Set the affixes |
457 // Always call the setter, even if the prefixes are empty, especially in
the case of the | 520 // Always call the setter, even if the prefixes are empty, especially in
the case of the |
458 // negative prefix pattern, to prevent default values from overriding th
e pattern. | 521 // negative prefix pattern, to prevent default values from overriding th
e pattern. |
459 properties.setPositivePrefixPattern(positive.prefix); | 522 properties.setPositivePrefixPattern(positive.prefix.toString()); |
460 properties.setPositiveSuffixPattern(positive.suffix); | 523 properties.setPositiveSuffixPattern(positive.suffix.toString()); |
461 if (negative != null) { | 524 if (negative != null) { |
462 properties.setNegativePrefixPattern(negative.prefix); | 525 properties.setNegativePrefixPattern(negative.prefix.toString()); |
463 properties.setNegativeSuffixPattern(negative.suffix); | 526 properties.setNegativeSuffixPattern(negative.suffix.toString()); |
464 } else { | 527 } else { |
465 properties.setNegativePrefixPattern(null); | 528 properties.setNegativePrefixPattern(null); |
466 properties.setNegativeSuffixPattern(null); | 529 properties.setNegativeSuffixPattern(null); |
467 } | 530 } |
468 | 531 |
469 // Set the magnitude multiplier | 532 // Set the magnitude multiplier |
470 if (positive.hasPercentSign) { | 533 if (positive.hasPercentSign) { |
471 properties.setMagnitudeMultiplier(2); | 534 properties.setMagnitudeMultiplier(2); |
472 } else if (positive.hasPerMilleSign) { | 535 } else if (positive.hasPerMilleSign) { |
473 properties.setMagnitudeMultiplier(3); | 536 properties.setMagnitudeMultiplier(3); |
474 } else { | 537 } else { |
475 properties.setMagnitudeMultiplier(Properties.DEFAULT_MAGNITUDE_MULTIPL
IER); | 538 properties.setMagnitudeMultiplier(Properties.DEFAULT_MAGNITUDE_MULTIPL
IER); |
476 } | 539 } |
477 } | 540 } |
478 } | 541 } |
479 | 542 |
480 private static class SubpatternParseResult { | 543 private static class SubpatternParseResult { |
481 int[] groupingSizes = new int[] {0, -1, -1}; | 544 int[] groupingSizes = new int[] {0, -1, -1}; |
482 int minimumIntegerDigits = 0; | 545 int minimumIntegerDigits = 0; |
483 int totalIntegerDigits = 0; | 546 int totalIntegerDigits = 0; |
484 int minimumFractionDigits = 0; | 547 int minimumFractionDigits = 0; |
485 int maximumFractionDigits = 0; | 548 int maximumFractionDigits = 0; |
486 int minimumSignificantDigits = 0; | 549 int minimumSignificantDigits = 0; |
487 int maximumSignificantDigits = 0; | 550 int maximumSignificantDigits = 0; |
488 boolean hasDecimal = false; | 551 boolean hasDecimal = false; |
489 int paddingWidth = 0; | 552 int paddingWidth = 0; |
490 PaddingLocation paddingLocation = null; | 553 PadPosition paddingLocation = null; |
491 FormatQuantity4 rounding = new FormatQuantity4(); | 554 FormatQuantity4 rounding = new FormatQuantity4(); |
492 boolean exponentShowPlusSign = false; | 555 boolean exponentShowPlusSign = false; |
493 int exponentDigits = 0; | 556 int exponentDigits = 0; |
494 boolean hasPercentSign = false; | 557 boolean hasPercentSign = false; |
495 boolean hasPerMilleSign = false; | 558 boolean hasPerMilleSign = false; |
496 boolean hasCurrencySign = false; | 559 boolean hasCurrencySign = false; |
497 | 560 |
498 StringBuilder padding = new StringBuilder(); | 561 StringBuilder padding = new StringBuilder(); |
499 StringBuilder prefix = new StringBuilder(); | 562 StringBuilder prefix = new StringBuilder(); |
500 StringBuilder suffix = new StringBuilder(); | 563 StringBuilder suffix = new StringBuilder(); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
544 | 607 |
545 static void parse(String pattern, Properties properties, boolean ignoreRound
ing) { | 608 static void parse(String pattern, Properties properties, boolean ignoreRound
ing) { |
546 if (pattern == null || pattern.length() == 0) { | 609 if (pattern == null || pattern.length() == 0) { |
547 // Backwards compatibility requires that we reset to the default values. | 610 // Backwards compatibility requires that we reset to the default values. |
548 // TODO: Only overwrite the properties that "saveToProperties" normally
touches? | 611 // TODO: Only overwrite the properties that "saveToProperties" normally
touches? |
549 properties.clear(); | 612 properties.clear(); |
550 return; | 613 return; |
551 } | 614 } |
552 | 615 |
553 // TODO: Use whitespace characters from PatternProps | 616 // TODO: Use whitespace characters from PatternProps |
| 617 // TODO: Use thread locals here. |
554 ParserState state = new ParserState(pattern); | 618 ParserState state = new ParserState(pattern); |
555 PatternParseResult result = new PatternParseResult(); | 619 PatternParseResult result = new PatternParseResult(); |
556 consumePattern(state, result); | 620 consumePattern(state, result); |
557 result.saveToProperties(properties, ignoreRounding); | 621 result.saveToProperties(properties, ignoreRounding); |
558 } | 622 } |
559 | 623 |
560 private static void consumePattern(ParserState state, PatternParseResult res
ult) { | 624 private static void consumePattern(ParserState state, PatternParseResult res
ult) { |
561 // pattern := subpattern (';' subpattern)? | 625 // pattern := subpattern (';' subpattern)? |
562 consumeSubpattern(state, result.positive); | 626 consumeSubpattern(state, result.positive); |
563 if (state.peek() == ';') { | 627 if (state.peek() == ';') { |
564 state.next(); // consume the ';' | 628 state.next(); // consume the ';' |
565 result.negative = new SubpatternParseResult(); | 629 result.negative = new SubpatternParseResult(); |
566 consumeSubpattern(state, result.negative); | 630 consumeSubpattern(state, result.negative); |
567 } | 631 } |
568 if (state.peek() != -1) { | 632 if (state.peek() != -1) { |
569 throw state.toParseException("pattern"); | 633 throw state.toParseException("pattern"); |
570 } | 634 } |
571 } | 635 } |
572 | 636 |
573 private static void consumeSubpattern(ParserState state, SubpatternParseResu
lt result) { | 637 private static void consumeSubpattern(ParserState state, SubpatternParseResu
lt result) { |
574 // subpattern := literals? number exponent? literals? | 638 // subpattern := literals? number exponent? literals? |
575 consumePadding(state, result, PaddingLocation.BEFORE_PREFIX); | 639 consumePadding(state, result, PadPosition.BEFORE_PREFIX); |
576 consumeAffix(state, result, result.prefix); | 640 consumeAffix(state, result, result.prefix); |
577 consumePadding(state, result, PaddingLocation.AFTER_PREFIX); | 641 consumePadding(state, result, PadPosition.AFTER_PREFIX); |
578 consumeFormat(state, result); | 642 consumeFormat(state, result); |
579 consumeExponent(state, result); | 643 consumeExponent(state, result); |
580 consumePadding(state, result, PaddingLocation.BEFORE_SUFFIX); | 644 consumePadding(state, result, PadPosition.BEFORE_SUFFIX); |
581 consumeAffix(state, result, result.suffix); | 645 consumeAffix(state, result, result.suffix); |
582 consumePadding(state, result, PaddingLocation.AFTER_SUFFIX); | 646 consumePadding(state, result, PadPosition.AFTER_SUFFIX); |
583 } | 647 } |
584 | 648 |
585 private static void consumePadding( | 649 private static void consumePadding( |
586 ParserState state, SubpatternParseResult result, PaddingLocation padding
Location) { | 650 ParserState state, SubpatternParseResult result, PadPosition paddingLoca
tion) { |
587 if (state.peek() != '*') { | 651 if (state.peek() != '*') { |
588 return; | 652 return; |
589 } | 653 } |
590 result.paddingLocation = paddingLocation; | 654 result.paddingLocation = paddingLocation; |
591 state.next(); // consume the '*' | 655 state.next(); // consume the '*' |
592 consumeLiteral(state, result.padding); | 656 consumeLiteral(state, result.padding); |
593 } | 657 } |
594 | 658 |
595 private static void consumeAffix( | 659 private static void consumeAffix( |
596 ParserState state, SubpatternParseResult result, StringBuilder destinati
on) { | 660 ParserState state, SubpatternParseResult result, StringBuilder destinati
on) { |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
782 result.paddingWidth++; | 846 result.paddingWidth++; |
783 } | 847 } |
784 while (state.peek() == '0') { | 848 while (state.peek() == '0') { |
785 state.next(); // consume the 0 | 849 state.next(); // consume the 0 |
786 result.exponentDigits += 1; | 850 result.exponentDigits += 1; |
787 result.paddingWidth++; | 851 result.paddingWidth++; |
788 } | 852 } |
789 } | 853 } |
790 } | 854 } |
791 } | 855 } |
LEFT | RIGHT |