| 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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 /** Caches the symbols used for our locale. */ | 75 /** Caches the symbols used for our locale. */ |
| 76 NumberSymbols _symbols; | 76 NumberSymbols _symbols; |
| 77 | 77 |
| 78 /** | 78 /** |
| 79 * Transient internal state in which to build up the result of the format | 79 * Transient internal state in which to build up the result of the format |
| 80 * operation. We can have this be just an instance variable because Dart is | 80 * operation. We can have this be just an instance variable because Dart is |
| 81 * single-threaded and unless we do an asynchronous operation in the process | 81 * single-threaded and unless we do an asynchronous operation in the process |
| 82 * of formatting then there will only ever be one number being formatted | 82 * of formatting then there will only ever be one number being formatted |
| 83 * at a time. In languages with threads we'd need to pass this on the stack. | 83 * at a time. In languages with threads we'd need to pass this on the stack. |
| 84 */ | 84 */ |
| 85 StringBuffer _buffer; | 85 final StringBuffer _buffer = new StringBuffer(); |
| 86 | 86 |
| 87 /** | 87 /** |
| 88 * Create a number format that prints using [newPattern] as it applies in | 88 * Create a number format that prints using [newPattern] as it applies in |
| 89 * [locale]. | 89 * [locale]. |
| 90 */ | 90 */ |
| 91 factory NumberFormat([String newPattern, String locale]) => | 91 factory NumberFormat([String newPattern, String locale]) => |
| 92 new NumberFormat._forPattern(locale, (x) => newPattern); | 92 new NumberFormat._forPattern(locale, (x) => newPattern); |
| 93 | 93 |
| 94 /** Create a number format that prints as DECIMAL_PATTERN. */ | 94 /** Create a number format that prints as DECIMAL_PATTERN. */ |
| 95 NumberFormat.decimalPattern([String locale]) : | 95 NumberFormat.decimalPattern([String locale]) : |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 139 | 139 |
| 140 /** | 140 /** |
| 141 * Format [number] according to our pattern and return the formatted string. | 141 * Format [number] according to our pattern and return the formatted string. |
| 142 */ | 142 */ |
| 143 String format(num number) { | 143 String format(num number) { |
| 144 // TODO(alanknight): Do we have to do anything for printing numbers bidi? | 144 // TODO(alanknight): Do we have to do anything for printing numbers bidi? |
| 145 // Or are they always printed left to right? | 145 // Or are they always printed left to right? |
| 146 if (number.isNaN) return symbols.NAN; | 146 if (number.isNaN) return symbols.NAN; |
| 147 if (number.isInfinite) return "${_signPrefix(number)}${symbols.INFINITY}"; | 147 if (number.isInfinite) return "${_signPrefix(number)}${symbols.INFINITY}"; |
| 148 | 148 |
| 149 _newBuffer(); | |
| 150 _add(_signPrefix(number)); | 149 _add(_signPrefix(number)); |
| 151 _formatNumber(number.abs() * _multiplier); | 150 _formatNumber(number.abs() * _multiplier); |
| 152 _add(_signSuffix(number)); | 151 _add(_signSuffix(number)); |
| 153 | 152 |
| 154 var result = _buffer.toString(); | 153 var result = _buffer.toString(); |
| 155 _buffer = null; | 154 _buffer.clear(); |
| 156 return result; | 155 return result; |
| 157 } | 156 } |
| 158 | 157 |
| 159 /** | 158 /** |
| 160 * Format the main part of the number in the form dictated by the pattern. | 159 * Format the main part of the number in the form dictated by the pattern. |
| 161 */ | 160 */ |
| 162 void _formatNumber(num number) { | 161 void _formatNumber(num number) { |
| 163 if (_useExponentialNotation) { | 162 if (_useExponentialNotation) { |
| 164 _formatExponential(number); | 163 _formatExponential(number); |
| 165 } else { | 164 } else { |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 fracValue = 0; | 242 fracValue = 0; |
| 244 } else { | 243 } else { |
| 245 intValue = shiftedNumber.round() ~/ power; | 244 intValue = shiftedNumber.round() ~/ power; |
| 246 fracValue = (shiftedNumber - intValue * power).floor(); | 245 fracValue = (shiftedNumber - intValue * power).floor(); |
| 247 } | 246 } |
| 248 var fractionPresent = minimumFractionDigits > 0 || fracValue > 0; | 247 var fractionPresent = minimumFractionDigits > 0 || fracValue > 0; |
| 249 | 248 |
| 250 // If the int part is larger than 2^52 and we're on Javascript (so it's | 249 // If the int part is larger than 2^52 and we're on Javascript (so it's |
| 251 // really a float) it will lose precision, so pad out the rest of it | 250 // really a float) it will lose precision, so pad out the rest of it |
| 252 // with zeros. Check for Javascript by seeing if an integer is double. | 251 // with zeros. Check for Javascript by seeing if an integer is double. |
| 253 var paddingDigits = new StringBuffer(); | 252 var paddingDigits = ''; |
| 254 if (1 is double && intValue > _maxInt) { | 253 if (1 is double && intValue > _maxInt) { |
| 255 var howManyDigitsTooBig = (log(intValue) / LN10).ceil() - 16; | 254 var howManyDigitsTooBig = (log(intValue) / LN10).ceil() - 16; |
| 256 var divisor = pow(10, howManyDigitsTooBig).round(); | 255 var divisor = pow(10, howManyDigitsTooBig).round(); |
| 257 for (var each in new List(howManyDigitsTooBig.toInt())) { | 256 paddingDigits = symbols.ZERO_DIGIT * howManyDigitsTooBig.toInt(); |
| 258 paddingDigits.write(symbols.ZERO_DIGIT); | 257 |
| 259 } | |
| 260 intValue = (intValue / divisor).truncate(); | 258 intValue = (intValue / divisor).truncate(); |
| 261 } | 259 } |
| 262 var integerDigits = "${intValue}${paddingDigits}".codeUnits; | 260 var integerDigits = "${intValue}${paddingDigits}".codeUnits; |
| 263 var digitLength = integerDigits.length; | 261 var digitLength = integerDigits.length; |
| 264 | 262 |
| 265 if (_hasPrintableIntegerPart(intValue)) { | 263 if (_hasPrintableIntegerPart(intValue)) { |
| 266 _pad(minimumIntegerDigits - digitLength); | 264 _pad(minimumIntegerDigits - digitLength); |
| 267 for (var i = 0; i < digitLength; i++) { | 265 for (var i = 0; i < digitLength; i++) { |
| 268 _addDigit(integerDigits[i]); | 266 _addDigit(integerDigits[i]); |
| 269 _group(digitLength, i); | 267 _group(digitLength, i); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 300 } | 298 } |
| 301 | 299 |
| 302 /** | 300 /** |
| 303 * Return true if we have a main integer part which is printable, either | 301 * Return true if we have a main integer part which is printable, either |
| 304 * because we have digits left of the decimal point, or because there are | 302 * because we have digits left of the decimal point, or because there are |
| 305 * a minimum number of printable digits greater than 1. | 303 * a minimum number of printable digits greater than 1. |
| 306 */ | 304 */ |
| 307 bool _hasPrintableIntegerPart(int intValue) => | 305 bool _hasPrintableIntegerPart(int intValue) => |
| 308 intValue > 0 || minimumIntegerDigits > 0; | 306 intValue > 0 || minimumIntegerDigits > 0; |
| 309 | 307 |
| 310 /** | |
| 311 * Create a new empty buffer. See comment on [_buffer] variable for why | |
| 312 * we have it as an instance variable rather than passing it on the stack. | |
| 313 */ | |
| 314 void _newBuffer() { _buffer = new StringBuffer(); } | |
| 315 | |
| 316 /** A group of methods that provide support for writing digits and other | 308 /** A group of methods that provide support for writing digits and other |
| 317 * required characters into [_buffer] easily. | 309 * required characters into [_buffer] easily. |
| 318 */ | 310 */ |
| 319 void _add(String x) { _buffer.write(x);} | 311 void _add(String x) { _buffer.write(x);} |
| 320 void _addCharCode(int x) { _buffer.writeCharCode(x); } | 312 void _addCharCode(int x) { _buffer.writeCharCode(x); } |
| 321 void _addZero() { _buffer.write(symbols.ZERO_DIGIT); } | 313 void _addZero() { _buffer.write(symbols.ZERO_DIGIT); } |
| 322 void _addDigit(int x) { _buffer.writeCharCode(_localeZero + x - _zero); } | 314 void _addDigit(int x) { _buffer.writeCharCode(_localeZero + x - _zero); } |
| 323 | 315 |
| 324 /** Print padding up to [numberOfDigits] above what's included in [basic]. */ | 316 /** Print padding up to [numberOfDigits] above what's included in [basic]. */ |
| 325 void _pad(int numberOfDigits, [String basic = '']) { | 317 void _pad(int numberOfDigits, [String basic = '']) { |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 pattern.moveNext(); | 432 pattern.moveNext(); |
| 441 } | 433 } |
| 442 format._negativeSuffix = _parseAffix(); | 434 format._negativeSuffix = _parseAffix(); |
| 443 } else { | 435 } else { |
| 444 // If no negative affix is specified, they share the same positive affix. | 436 // If no negative affix is specified, they share the same positive affix. |
| 445 format._negativePrefix = format._positivePrefix + format._negativePrefix; | 437 format._negativePrefix = format._positivePrefix + format._negativePrefix; |
| 446 format._negativeSuffix = format._negativeSuffix + format._positiveSuffix; | 438 format._negativeSuffix = format._negativeSuffix + format._positiveSuffix; |
| 447 } | 439 } |
| 448 } | 440 } |
| 449 | 441 |
| 450 /** Variable used in parsing prefixes and suffixes to keep track of | 442 /** |
| 451 * whether or not we are in a quoted region. */ | 443 * Variable used in parsing prefixes and suffixes to keep track of |
| 444 * whether or not we are in a quoted region. |
| 445 */ |
| 452 bool inQuote = false; | 446 bool inQuote = false; |
| 453 | 447 |
| 454 /** | 448 /** |
| 455 * Parse a prefix or suffix and return the prefix/suffix string. Note that | 449 * Parse a prefix or suffix and return the prefix/suffix string. Note that |
| 456 * this also may modify the state of [format]. | 450 * this also may modify the state of [format]. |
| 457 */ | 451 */ |
| 458 String _parseAffix() { | 452 String _parseAffix() { |
| 459 var affix = new StringBuffer(); | 453 var affix = new StringBuffer(); |
| 460 inQuote = false; | 454 inQuote = false; |
| 461 while (parseCharacterAffix(affix) && pattern.moveNext()); | 455 while (parseCharacterAffix(affix) && pattern.moveNext()); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 affix.write(symbols.PERMILL); | 503 affix.write(symbols.PERMILL); |
| 510 break; | 504 break; |
| 511 default: | 505 default: |
| 512 affix.write(ch); | 506 affix.write(ch); |
| 513 } | 507 } |
| 514 } | 508 } |
| 515 return true; | 509 return true; |
| 516 } | 510 } |
| 517 | 511 |
| 518 /** Variables used in [_parseTrunk] and [parseTrunkCharacter]. */ | 512 /** Variables used in [_parseTrunk] and [parseTrunkCharacter]. */ |
| 519 var decimalPos; | 513 var decimalPos = -1; |
| 520 var digitLeftCount; | 514 var digitLeftCount = 0; |
| 521 var zeroDigitCount; | 515 var zeroDigitCount = 0; |
| 522 var digitRightCount; | 516 var digitRightCount = 0; |
| 523 var groupingCount; | 517 var groupingCount = -1; |
| 524 var trunk; | |
| 525 | 518 |
| 526 /** | 519 /** |
| 527 * Parse the "trunk" portion of the pattern, the piece that doesn't include | 520 * Parse the "trunk" portion of the pattern, the piece that doesn't include |
| 528 * positive or negative prefixes or suffixes. | 521 * positive or negative prefixes or suffixes. |
| 529 */ | 522 */ |
| 530 String _parseTrunk() { | 523 String _parseTrunk() { |
| 531 decimalPos = -1; | |
| 532 digitLeftCount = 0; | |
| 533 zeroDigitCount = 0; | |
| 534 digitRightCount = 0; | |
| 535 groupingCount = -1; | |
| 536 | |
| 537 var loop = true; | 524 var loop = true; |
| 538 trunk = new StringBuffer(); | 525 var trunk = new StringBuffer(); |
| 539 while (pattern.current != null && loop) { | 526 while (pattern.current != null && loop) { |
| 540 loop = parseTrunkCharacter(); | 527 loop = parseTrunkCharacter(trunk); |
| 541 } | 528 } |
| 542 | 529 |
| 543 if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { | 530 if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { |
| 544 // Handle '###.###' and '###.' and '.###' | 531 // Handle '###.###' and '###.' and '.###' |
| 545 // Handle '.###' | 532 // Handle '.###' |
| 546 var n = decimalPos == 0 ? 1 : decimalPos; | 533 var n = decimalPos == 0 ? 1 : decimalPos; |
| 547 digitRightCount = digitLeftCount - n; | 534 digitRightCount = digitLeftCount - n; |
| 548 digitLeftCount = n - 1; | 535 digitLeftCount = n - 1; |
| 549 zeroDigitCount = 1; | 536 zeroDigitCount = 1; |
| 550 } | 537 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 589 decimalPos == totalDigits; | 576 decimalPos == totalDigits; |
| 590 | 577 |
| 591 return trunk.toString(); | 578 return trunk.toString(); |
| 592 } | 579 } |
| 593 | 580 |
| 594 /** | 581 /** |
| 595 * Parse an individual character of the trunk. Return true if we should | 582 * Parse an individual character of the trunk. Return true if we should |
| 596 * continue to look for additional trunk characters or false if we have | 583 * continue to look for additional trunk characters or false if we have |
| 597 * reached the end. | 584 * reached the end. |
| 598 */ | 585 */ |
| 599 bool parseTrunkCharacter() { | 586 bool parseTrunkCharacter(trunk) { |
| 600 var ch = pattern.current; | 587 var ch = pattern.current; |
| 601 switch (ch) { | 588 switch (ch) { |
| 602 case _PATTERN_DIGIT: | 589 case _PATTERN_DIGIT: |
| 603 if (zeroDigitCount > 0) { | 590 if (zeroDigitCount > 0) { |
| 604 digitRightCount++; | 591 digitRightCount++; |
| 605 } else { | 592 } else { |
| 606 digitLeftCount++; | 593 digitLeftCount++; |
| 607 } | 594 } |
| 608 if (groupingCount >= 0 && decimalPos < 0) { | 595 if (groupingCount >= 0 && decimalPos < 0) { |
| 609 groupingCount++; | 596 groupingCount++; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 690 | 677 |
| 691 _StringIterable(String s) : iterator = _iterator(s); | 678 _StringIterable(String s) : iterator = _iterator(s); |
| 692 } | 679 } |
| 693 | 680 |
| 694 /** | 681 /** |
| 695 * Provides an iterator over a string as a list of substrings, and also | 682 * Provides an iterator over a string as a list of substrings, and also |
| 696 * gives us a lookahead of one via the [peek] method. | 683 * gives us a lookahead of one via the [peek] method. |
| 697 */ | 684 */ |
| 698 class _StringIterator implements Iterator<String> { | 685 class _StringIterator implements Iterator<String> { |
| 699 final String input; | 686 final String input; |
| 700 int index = -1; | 687 int nextIndex = 0; |
| 701 inBounds(i) => i >= 0 && i < input.length; | 688 String _current = null; |
| 702 _StringIterator(this.input); | |
| 703 String get current => inBounds(index) ? input[index] : null; | |
| 704 | 689 |
| 705 bool moveNext() => inBounds(++index); | 690 _StringIterator(input) : input = _validate(input); |
| 706 String get peek => inBounds(index + 1) ? input[index + 1] : null; | 691 |
| 692 String get current => _current; |
| 693 |
| 694 bool moveNext() { |
| 695 if (nextIndex >= input.length) { |
| 696 _current = null; |
| 697 return false; |
| 698 } |
| 699 _current = input[nextIndex++]; |
| 700 return true; |
| 701 } |
| 702 |
| 703 String get peek => nextIndex >= input.length ? null : input[nextIndex]; |
| 704 |
| 707 Iterator<String> get iterator => this; | 705 Iterator<String> get iterator => this; |
| 706 |
| 707 static String _validate(input) { |
| 708 if (input is! String) throw new ArgumentError(input); |
| 709 return input; |
| 710 } |
| 711 |
| 708 } | 712 } |
| OLD | NEW |