OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of intl; | 5 part of intl; |
6 /** | 6 /** |
7 * Provides the ability to format a number in a locale-specific way. The | 7 * Provides the ability to format a number in a locale-specific way. The |
8 * format is specified as a pattern using a subset of the ICU formatting | 8 * format is specified as a pattern using a subset of the ICU formatting |
9 * patterns. | 9 * patterns. |
10 * | 10 * |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 bool _decimalSeparatorAlwaysShown = false; | 64 bool _decimalSeparatorAlwaysShown = false; |
65 bool _useSignForPositiveExponent = false; | 65 bool _useSignForPositiveExponent = false; |
66 bool _useExponentialNotation = false; | 66 bool _useExponentialNotation = false; |
67 | 67 |
68 int maximumIntegerDigits = 40; | 68 int maximumIntegerDigits = 40; |
69 int minimumIntegerDigits = 1; | 69 int minimumIntegerDigits = 1; |
70 int maximumFractionDigits = 3; | 70 int maximumFractionDigits = 3; |
71 int minimumFractionDigits = 0; | 71 int minimumFractionDigits = 0; |
72 int minimumExponentDigits = 0; | 72 int minimumExponentDigits = 0; |
73 | 73 |
74 int _multiplier = 1; | 74 /** |
| 75 * For percent and permille, what are we multiplying by in order to |
| 76 * get the printed value, e.g. 100 for percent. |
| 77 */ |
| 78 int get _multiplier => _internalMultiplier; |
| 79 set _multiplier(int x) { |
| 80 _internalMultiplier = x; |
| 81 _multiplierDigits = (log(_multiplier) / LN10).round(); |
| 82 } |
| 83 int _internalMultiplier = 1; |
| 84 |
| 85 /** How many digits are there in the [_multiplier]. */ |
| 86 int _multiplierDigits = 0; |
75 | 87 |
76 /** | 88 /** |
77 * Stores the pattern used to create this format. This isn't used, but | 89 * Stores the pattern used to create this format. This isn't used, but |
78 * is helpful in debugging. | 90 * is helpful in debugging. |
79 */ | 91 */ |
80 String _pattern; | 92 String _pattern; |
81 | 93 |
82 /** The locale in which we print numbers. */ | 94 /** The locale in which we print numbers. */ |
83 final String _locale; | 95 final String _locale; |
84 | 96 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 | 167 |
156 /** | 168 /** |
157 * Return the symbols which are used in our locale. Cache them to avoid | 169 * Return the symbols which are used in our locale. Cache them to avoid |
158 * repeated lookup. | 170 * repeated lookup. |
159 */ | 171 */ |
160 NumberSymbols get symbols => _symbols; | 172 NumberSymbols get symbols => _symbols; |
161 | 173 |
162 /** | 174 /** |
163 * Format [number] according to our pattern and return the formatted string. | 175 * Format [number] according to our pattern and return the formatted string. |
164 */ | 176 */ |
165 String format(num number) { | 177 String format(number) { |
166 // TODO(alanknight): Do we have to do anything for printing numbers bidi? | 178 if (_isNaN(number)) return symbols.NAN; |
167 // Or are they always printed left to right? | 179 if (_isInfinite(number)) return "${_signPrefix(number)}${symbols.INFINITY}"; |
168 if (number.isNaN) return symbols.NAN; | |
169 if (number.isInfinite) return "${_signPrefix(number)}${symbols.INFINITY}"; | |
170 | 180 |
171 _add(_signPrefix(number)); | 181 _add(_signPrefix(number)); |
172 _formatNumber(number.abs() * _multiplier); | 182 _formatNumber(number.abs()); |
173 _add(_signSuffix(number)); | 183 _add(_signSuffix(number)); |
174 | 184 |
175 var result = _buffer.toString(); | 185 var result = _buffer.toString(); |
176 _buffer.clear(); | 186 _buffer.clear(); |
177 return result; | 187 return result; |
178 } | 188 } |
179 | 189 |
180 /** | 190 /** |
181 * Parse the number represented by the string. If it's not | 191 * Parse the number represented by the string. If it's not |
182 * parseable, throws a [FormatException]. | 192 * parseable, throws a [FormatException]. |
183 */ | 193 */ |
184 num parse(String text) => new _NumberParser(this, text).value; | 194 num parse(String text) => new _NumberParser(this, text).value; |
185 | 195 |
186 /** | 196 /** |
187 * Format the main part of the number in the form dictated by the pattern. | 197 * Format the main part of the number in the form dictated by the pattern. |
188 */ | 198 */ |
189 void _formatNumber(num number) { | 199 void _formatNumber(number) { |
190 if (_useExponentialNotation) { | 200 if (_useExponentialNotation) { |
191 _formatExponential(number); | 201 _formatExponential(number); |
192 } else { | 202 } else { |
193 _formatFixed(number); | 203 _formatFixed(number); |
194 } | 204 } |
195 } | 205 } |
196 | 206 |
197 /** Format the number in exponential notation. */ | 207 /** Format the number in exponential notation. */ |
198 void _formatExponential(num number) { | 208 void _formatExponential(num number) { |
199 if (number == 0.0) { | 209 if (number == 0.0) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 } else if (_useSignForPositiveExponent) { | 253 } else if (_useSignForPositiveExponent) { |
244 _add(symbols.PLUS_SIGN); | 254 _add(symbols.PLUS_SIGN); |
245 } | 255 } |
246 _pad(minimumExponentDigits, exponent.toString()); | 256 _pad(minimumExponentDigits, exponent.toString()); |
247 } | 257 } |
248 | 258 |
249 /** Used to test if we have exceeded Javascript integer limits. */ | 259 /** Used to test if we have exceeded Javascript integer limits. */ |
250 final _maxInt = pow(2, 52); | 260 final _maxInt = pow(2, 52); |
251 | 261 |
252 /** | 262 /** |
| 263 * Helpers to check numbers that don't conform to the [num] interface, |
| 264 * e.g. Int64 |
| 265 */ |
| 266 _isInfinite(number) => number is num ? number.isInfinite : false; |
| 267 _isNaN(number) => number is num ? number.isNaN : false; |
| 268 _round(number) => number is num ? number.round() : number; |
| 269 _floor(number) => number is num ? number.floor() : number; |
| 270 |
| 271 /** |
253 * Format the basic number portion, inluding the fractional digits. | 272 * Format the basic number portion, inluding the fractional digits. |
254 */ | 273 */ |
255 void _formatFixed(num number) { | 274 void _formatFixed(number) { |
256 // Very fussy math to get integer and fractional parts. | 275 var integerPart; |
257 var power = pow(10, maximumFractionDigits); | 276 int fractionPart; |
258 var shiftedNumber = (number * power); | 277 int extraIntegerDigits; |
259 // We must not roundToDouble() an int or it will lose precision. We must not | 278 |
260 // round() a large double or it will take its loss of precision and | 279 final power = pow(10, maximumFractionDigits); |
261 // preserve it in an int, which we will then print to the right | 280 final digitMultiplier = power * _multiplier; |
262 // of the decimal place. Therefore, only roundToDouble if we are already | 281 |
263 // a double. | 282 if (_isInfinite(number)) { |
264 if (shiftedNumber is double) { | 283 integerPart = number.toInt(); |
265 shiftedNumber = shiftedNumber.roundToDouble(); | 284 extraIntegerDigits = 0; |
| 285 fractionPart = 0; |
| 286 } else { |
| 287 // We have three possible pieces. First, the basic integer part. If this |
| 288 // is a percent or permille, the additional 2 or 3 digits. Finally the |
| 289 // fractional part. |
| 290 // We avoid multiplying the number because it might overflow if we have |
| 291 // a fixed-size integer type, so we extract each of the three as an |
| 292 // integer pieces. |
| 293 integerPart = _floor(number); |
| 294 var fraction = number - integerPart; |
| 295 // Multiply out to the number of decimal places and the percent, then |
| 296 // round. For fixed-size integer types this should always be zero, so |
| 297 // multiplying is OK. |
| 298 var remainingDigits = _round(fraction * digitMultiplier).toInt(); |
| 299 // However, in rounding we may overflow into the main digits. |
| 300 if (remainingDigits >= digitMultiplier) { |
| 301 integerPart++; |
| 302 remainingDigits -= digitMultiplier; |
| 303 } |
| 304 // Separate out the extra integer parts from the fraction part. |
| 305 extraIntegerDigits = remainingDigits ~/ power; |
| 306 fractionPart = remainingDigits % power; |
266 } | 307 } |
267 var intValue, fracValue; | 308 var fractionPresent = minimumFractionDigits > 0 || fractionPart > 0; |
268 if (shiftedNumber.isInfinite) { | |
269 intValue = number.toInt(); | |
270 fracValue = 0; | |
271 } else { | |
272 intValue = shiftedNumber.round() ~/ power; | |
273 fracValue = (shiftedNumber - intValue * power).floor(); | |
274 } | |
275 var fractionPresent = minimumFractionDigits > 0 || fracValue > 0; | |
276 | 309 |
277 // If the int part is larger than 2^52 and we're on Javascript (so it's | 310 var integerDigits = _integerDigits(integerPart, extraIntegerDigits); |
278 // really a float) it will lose precision, so pad out the rest of it | |
279 // with zeros. Check for Javascript by seeing if an integer is double. | |
280 var paddingDigits = ''; | |
281 if (1 is double && intValue > _maxInt) { | |
282 var howManyDigitsTooBig = (log(intValue) / LN10).ceil() - 16; | |
283 var divisor = pow(10, howManyDigitsTooBig).round(); | |
284 paddingDigits = symbols.ZERO_DIGIT * howManyDigitsTooBig.toInt(); | |
285 | |
286 intValue = (intValue / divisor).truncate(); | |
287 } | |
288 var integerDigits = "${intValue}${paddingDigits}".codeUnits; | |
289 var digitLength = integerDigits.length; | 311 var digitLength = integerDigits.length; |
290 | 312 |
291 if (_hasPrintableIntegerPart(intValue)) { | 313 if (_hasPrintableIntegerPart(integerPart)) { |
292 _pad(minimumIntegerDigits - digitLength); | 314 _pad(minimumIntegerDigits - digitLength); |
293 for (var i = 0; i < digitLength; i++) { | 315 for (var i = 0; i < digitLength; i++) { |
294 _addDigit(integerDigits[i]); | 316 _addDigit(integerDigits.codeUnitAt(i)); |
295 _group(digitLength, i); | 317 _group(digitLength, i); |
296 } | 318 } |
297 } else if (!fractionPresent) { | 319 } else if (!fractionPresent) { |
298 // If neither fraction nor integer part exists, just print zero. | 320 // If neither fraction nor integer part exists, just print zero. |
299 _addZero(); | 321 _addZero(); |
300 } | 322 } |
301 | 323 |
302 _decimalSeparator(fractionPresent); | 324 _decimalSeparator(fractionPresent); |
303 _formatFractionPart((fracValue + power).toString()); | 325 _formatFractionPart((fractionPart + power).toString()); |
304 } | 326 } |
305 | 327 |
306 /** | 328 /** |
| 329 * Compute the raw integer digits which will then be printed with |
| 330 * grouping and translated to localized digits. |
| 331 */ |
| 332 String _integerDigits(integerPart, extraIntegerDigits) { |
| 333 // If the int part is larger than 2^52 and we're on Javascript (so it's |
| 334 // really a float) it will lose precision, so pad out the rest of it |
| 335 // with zeros. Check for Javascript by seeing if an integer is double. |
| 336 var paddingDigits = ''; |
| 337 if (1 is double && integerPart is num && integerPart > _maxInt) { |
| 338 var howManyDigitsTooBig = (log(integerPart) / LN10).ceil() - 16; |
| 339 var divisor = pow(10, howManyDigitsTooBig).round(); |
| 340 paddingDigits = symbols.ZERO_DIGIT * howManyDigitsTooBig.toInt(); |
| 341 integerPart = (integerPart / divisor).truncate(); |
| 342 } |
| 343 |
| 344 var extra = extraIntegerDigits == 0 ? '' : extraIntegerDigits.toString(); |
| 345 var intDigits = _mainIntegerDigits(integerPart); |
| 346 var paddedExtra = |
| 347 intDigits.isEmpty ? extra : extra.padLeft(_multiplierDigits, '0'); |
| 348 return "${intDigits}${paddedExtra}${paddingDigits}"; |
| 349 } |
| 350 |
| 351 /** |
| 352 * The digit string of the integer part. This is the empty string if the |
| 353 * integer part is zero and otherwise is the toString() of the integer |
| 354 * part, stripping off any minus sign. |
| 355 */ |
| 356 String _mainIntegerDigits(integer) { |
| 357 if (integer == 0) return ''; |
| 358 var digits = integer.toString(); |
| 359 // If we have a fixed-length int representation, it can have a negative |
| 360 // number whose negation is also negative, e.g. 2^-63 in 64-bit. |
| 361 // Remove the minus sign. |
| 362 return digits.startsWith('-') ? digits.substring(1) : digits; |
| 363 } |
| 364 |
| 365 /** |
307 * Format the part after the decimal place in a fixed point number. | 366 * Format the part after the decimal place in a fixed point number. |
308 */ | 367 */ |
309 void _formatFractionPart(String fractionPart) { | 368 void _formatFractionPart(String fractionPart) { |
310 var fractionCodes = fractionPart.codeUnits; | 369 var fractionCodes = fractionPart.codeUnits; |
311 var fractionLength = fractionPart.length; | 370 var fractionLength = fractionPart.length; |
312 while (fractionCodes[fractionLength - 1] == _zero && | 371 while (fractionCodes[fractionLength - 1] == _zero && |
313 fractionLength > minimumFractionDigits + 1) { | 372 fractionLength > minimumFractionDigits + 1) { |
314 fractionLength--; | 373 fractionLength--; |
315 } | 374 } |
316 for (var i = 1; i < fractionLength; i++) { | 375 for (var i = 1; i < fractionLength; i++) { |
317 _addDigit(fractionCodes[i]); | 376 _addDigit(fractionCodes[i]); |
318 } | 377 } |
319 } | 378 } |
320 | 379 |
321 /** Print the decimal separator if appropriate. */ | 380 /** Print the decimal separator if appropriate. */ |
322 void _decimalSeparator(bool fractionPresent) { | 381 void _decimalSeparator(bool fractionPresent) { |
323 if (_decimalSeparatorAlwaysShown || fractionPresent) { | 382 if (_decimalSeparatorAlwaysShown || fractionPresent) { |
324 _add(symbols.DECIMAL_SEP); | 383 _add(symbols.DECIMAL_SEP); |
325 } | 384 } |
326 } | 385 } |
327 | 386 |
328 /** | 387 /** |
329 * Return true if we have a main integer part which is printable, either | 388 * Return true if we have a main integer part which is printable, either |
330 * because we have digits left of the decimal point, or because there are | 389 * because we have digits left of the decimal point, or because there are |
331 * a minimum number of printable digits greater than 1. | 390 * a minimum number of printable digits greater than 1. |
332 */ | 391 */ |
333 bool _hasPrintableIntegerPart(int intValue) => | 392 bool _hasPrintableIntegerPart(x) => |
334 intValue > 0 || minimumIntegerDigits > 0; | 393 x > 0 || minimumIntegerDigits > 0; |
335 | 394 |
336 /** A group of methods that provide support for writing digits and other | 395 /** A group of methods that provide support for writing digits and other |
337 * required characters into [_buffer] easily. | 396 * required characters into [_buffer] easily. |
338 */ | 397 */ |
339 void _add(String x) { _buffer.write(x);} | 398 void _add(String x) { _buffer.write(x);} |
340 void _addCharCode(int x) { _buffer.writeCharCode(x);} | 399 void _addCharCode(int x) { _buffer.writeCharCode(x);} |
341 void _addZero() { _buffer.write(symbols.ZERO_DIGIT);} | 400 void _addZero() { _buffer.write(symbols.ZERO_DIGIT);} |
342 void _addDigit(int x) { _buffer.writeCharCode(_localeZero + x - _zero);} | 401 void _addDigit(int x) { _buffer.writeCharCode(_localeZero + x - _zero);} |
343 | 402 |
344 /** Print padding up to [numberOfDigits] above what's included in [basic]. */ | 403 /** Print padding up to [numberOfDigits] above what's included in [basic]. */ |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 // Note that there is a slight risk of a locale's zero digit not fitting | 436 // Note that there is a slight risk of a locale's zero digit not fitting |
378 // into a single code unit, but it seems very unlikely, and if it did, | 437 // into a single code unit, but it seems very unlikely, and if it did, |
379 // there's a pretty good chance that our assumptions about being able to do | 438 // there's a pretty good chance that our assumptions about being able to do |
380 // arithmetic on it would also be invalid. | 439 // arithmetic on it would also be invalid. |
381 get _localeZero => symbols.ZERO_DIGIT.codeUnits.first; | 440 get _localeZero => symbols.ZERO_DIGIT.codeUnits.first; |
382 | 441 |
383 /** | 442 /** |
384 * Returns the prefix for [x] based on whether it's positive or negative. | 443 * Returns the prefix for [x] based on whether it's positive or negative. |
385 * In en_US this would be '' and '-' respectively. | 444 * In en_US this would be '' and '-' respectively. |
386 */ | 445 */ |
387 String _signPrefix(num x) => x.isNegative ? _negativePrefix : _positivePrefix; | 446 String _signPrefix(x) => x.isNegative ? _negativePrefix : _positivePrefix; |
388 | 447 |
389 /** | 448 /** |
390 * Returns the suffix for [x] based on wether it's positive or negative. | 449 * Returns the suffix for [x] based on wether it's positive or negative. |
391 * In en_US there are no suffixes for positive or negative. | 450 * In en_US there are no suffixes for positive or negative. |
392 */ | 451 */ |
393 String _signSuffix(num x) => x.isNegative ? _negativeSuffix : _positiveSuffix; | 452 String _signSuffix(x) => x.isNegative ? _negativeSuffix : _positiveSuffix; |
394 | 453 |
395 void _setPattern(String newPattern) { | 454 void _setPattern(String newPattern) { |
396 if (newPattern == null) return; | 455 if (newPattern == null) return; |
397 // Make spaces non-breaking | 456 // Make spaces non-breaking |
398 _pattern = newPattern.replaceAll(' ', '\u00a0'); | 457 _pattern = newPattern.replaceAll(' ', '\u00a0'); |
399 var parser = new _NumberFormatParser(this, newPattern, currencyName); | 458 var parser = new _NumberFormatParser(this, newPattern, currencyName); |
400 parser.parse(); | 459 parser.parse(); |
401 } | 460 } |
402 | 461 |
403 String toString() => "NumberFormat($_locale, $_pattern)"; | 462 String toString() => "NumberFormat($_locale, $_pattern)"; |
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1001 String get peek => nextIndex >= input.length ? null : input[nextIndex]; | 1060 String get peek => nextIndex >= input.length ? null : input[nextIndex]; |
1002 | 1061 |
1003 Iterator<String> get iterator => this; | 1062 Iterator<String> get iterator => this; |
1004 | 1063 |
1005 static String _validate(input) { | 1064 static String _validate(input) { |
1006 if (input is! String) throw new ArgumentError(input); | 1065 if (input is! String) throw new ArgumentError(input); |
1007 return input; | 1066 return input; |
1008 } | 1067 } |
1009 | 1068 |
1010 } | 1069 } |
OLD | NEW |