Chromium Code Reviews| 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 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 StringBuffer _buffer; |
| 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 return new NumberFormat._forPattern(locale, (x) => newPattern); | 92 new NumberFormat._forPattern(locale, (x) => newPattern); |
| 93 } | |
| 94 | 93 |
| 95 /** Create a number format that prints as DECIMAL_PATTERN. */ | 94 /** Create a number format that prints as DECIMAL_PATTERN. */ |
| 96 NumberFormat.decimalPattern([String locale]) : | 95 NumberFormat.decimalPattern([String locale]) : |
| 97 this._forPattern(locale, (x) => x.DECIMAL_PATTERN); | 96 this._forPattern(locale, (x) => x.DECIMAL_PATTERN); |
| 98 | 97 |
| 99 /** Create a number format that prints as PERCENT_PATTERN. */ | 98 /** Create a number format that prints as PERCENT_PATTERN. */ |
| 100 NumberFormat.percentPattern([String locale]) : | 99 NumberFormat.percentPattern([String locale]) : |
| 101 this._forPattern(locale, (x) => x.PERCENT_PATTERN); | 100 this._forPattern(locale, (x) => x.PERCENT_PATTERN); |
| 102 | 101 |
| 103 /** Create a number format that prints as SCIENTIFIC_PATTERN. */ | 102 /** Create a number format that prints as SCIENTIFIC_PATTERN. */ |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 129 */ | 128 */ |
| 130 static bool localeExists(localeName) { | 129 static bool localeExists(localeName) { |
| 131 if (localeName == null) return false; | 130 if (localeName == null) return false; |
| 132 return numberFormatSymbols.containsKey(localeName); | 131 return numberFormatSymbols.containsKey(localeName); |
| 133 } | 132 } |
| 134 | 133 |
| 135 /** | 134 /** |
| 136 * Return the symbols which are used in our locale. Cache them to avoid | 135 * Return the symbols which are used in our locale. Cache them to avoid |
| 137 * repeated lookup. | 136 * repeated lookup. |
| 138 */ | 137 */ |
| 139 NumberSymbols get symbols { | 138 NumberSymbols get symbols => _symbols; |
| 140 return _symbols; | |
| 141 } | |
| 142 | 139 |
| 143 /** | 140 /** |
| 144 * Format [number] according to our pattern and return the formatted string. | 141 * Format [number] according to our pattern and return the formatted string. |
| 145 */ | 142 */ |
| 146 String format(num number) { | 143 String format(num number) { |
| 147 // 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? |
| 148 // Or are they always printed left to right? | 145 // Or are they always printed left to right? |
| 149 if (number.isNaN) return symbols.NAN; | 146 if (number.isNaN) return symbols.NAN; |
| 150 if (number.isInfinite) return "${_signPrefix(number)}${symbols.INFINITY}"; | 147 if (number.isInfinite) return "${_signPrefix(number)}${symbols.INFINITY}"; |
| 151 | 148 |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 300 if (_decimalSeparatorAlwaysShown || fractionPresent) { | 297 if (_decimalSeparatorAlwaysShown || fractionPresent) { |
| 301 _add(symbols.DECIMAL_SEP); | 298 _add(symbols.DECIMAL_SEP); |
| 302 } | 299 } |
| 303 } | 300 } |
| 304 | 301 |
| 305 /** | 302 /** |
| 306 * Return true if we have a main integer part which is printable, either | 303 * Return true if we have a main integer part which is printable, either |
| 307 * because we have digits left of the decimal point, or because there are | 304 * because we have digits left of the decimal point, or because there are |
| 308 * a minimum number of printable digits greater than 1. | 305 * a minimum number of printable digits greater than 1. |
| 309 */ | 306 */ |
| 310 bool _hasPrintableIntegerPart(int intValue) { | 307 bool _hasPrintableIntegerPart(int intValue) => |
| 311 return intValue > 0 || minimumIntegerDigits > 0; | 308 intValue > 0 || minimumIntegerDigits > 0; |
| 312 } | |
| 313 | 309 |
| 314 /** | 310 /** |
| 315 * Create a new empty buffer. See comment on [_buffer] variable for why | 311 * Create a new empty buffer. See comment on [_buffer] variable for why |
| 316 * we have it as an instance variable rather than passing it on the stack. | 312 * we have it as an instance variable rather than passing it on the stack. |
| 317 */ | 313 */ |
| 318 void _newBuffer() { _buffer = new StringBuffer(); } | 314 void _newBuffer() { _buffer = new StringBuffer(); } |
| 319 | 315 |
| 320 /** A group of methods that provide support for writing digits and other | 316 /** A group of methods that provide support for writing digits and other |
| 321 * required characters into [_buffer] easily. | 317 * required characters into [_buffer] easily. |
| 322 */ | 318 */ |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 357 // Note that there is a slight risk of a locale's zero digit not fitting | 353 // Note that there is a slight risk of a locale's zero digit not fitting |
| 358 // into a single code unit, but it seems very unlikely, and if it did, | 354 // into a single code unit, but it seems very unlikely, and if it did, |
| 359 // there's a pretty good chance that our assumptions about being able to do | 355 // there's a pretty good chance that our assumptions about being able to do |
| 360 // arithmetic on it would also be invalid. | 356 // arithmetic on it would also be invalid. |
| 361 get _localeZero => symbols.ZERO_DIGIT.codeUnits.first; | 357 get _localeZero => symbols.ZERO_DIGIT.codeUnits.first; |
| 362 | 358 |
| 363 /** | 359 /** |
| 364 * Returns the prefix for [x] based on whether it's positive or negative. | 360 * Returns the prefix for [x] based on whether it's positive or negative. |
| 365 * In en_US this would be '' and '-' respectively. | 361 * In en_US this would be '' and '-' respectively. |
| 366 */ | 362 */ |
| 367 String _signPrefix(num x) { | 363 String _signPrefix(num x) => x.isNegative ? _negativePrefix : _positivePrefix; |
| 368 return x.isNegative ? _negativePrefix : _positivePrefix; | |
| 369 } | |
| 370 | 364 |
| 371 /** | 365 /** |
| 372 * Returns the suffix for [x] based on wether it's positive or negative. | 366 * Returns the suffix for [x] based on wether it's positive or negative. |
| 373 * In en_US there are no suffixes for positive or negative. | 367 * In en_US there are no suffixes for positive or negative. |
| 374 */ | 368 */ |
| 375 String _signSuffix(num x) { | 369 String _signSuffix(num x) => x.isNegative ? _negativeSuffix : _positiveSuffix; |
| 376 return x.isNegative ? _negativeSuffix : _positiveSuffix; | |
| 377 } | |
| 378 | 370 |
| 379 void _setPattern(String newPattern) { | 371 void _setPattern(String newPattern) { |
| 380 if (newPattern == null) return; | 372 if (newPattern == null) return; |
| 381 // Make spaces non-breaking | 373 // Make spaces non-breaking |
| 382 _pattern = newPattern.replaceAll(' ', '\u00a0'); | 374 _pattern = newPattern.replaceAll(' ', '\u00a0'); |
| 383 var parser = new _NumberFormatParser(this, newPattern); | 375 var parser = new _NumberFormatParser(this, newPattern); |
| 384 parser.parse(); | 376 parser.parse(); |
| 385 } | 377 } |
| 386 | 378 |
| 387 String toString() => "NumberFormat($_locale, $_pattern)"; | 379 String toString() => "NumberFormat($_locale, $_pattern)"; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 459 * whether or not we are in a quoted region. */ | 451 * whether or not we are in a quoted region. */ |
| 460 bool inQuote = false; | 452 bool inQuote = false; |
| 461 | 453 |
| 462 /** | 454 /** |
| 463 * Parse a prefix or suffix and return the prefix/suffix string. Note that | 455 * Parse a prefix or suffix and return the prefix/suffix string. Note that |
| 464 * this also may modify the state of [format]. | 456 * this also may modify the state of [format]. |
| 465 */ | 457 */ |
| 466 String _parseAffix() { | 458 String _parseAffix() { |
| 467 var affix = new StringBuffer(); | 459 var affix = new StringBuffer(); |
| 468 inQuote = false; | 460 inQuote = false; |
| 469 var loop = true; | 461 while (parseCharacterAffix(affix) && pattern.moveNext()); |
| 470 while (loop) { | |
| 471 loop = parseCharacterAffix(affix) && pattern.moveNext(); | |
| 472 } | |
| 473 return affix.toString(); | 462 return affix.toString(); |
| 474 } | 463 } |
| 475 | 464 |
| 476 /** | 465 /** |
| 477 * Parse an individual character as part of a prefix or suffix. Return true | 466 * Parse an individual character as part of a prefix or suffix. Return true |
| 478 * if we should continue to look for more affix characters, and false if | 467 * if we should continue to look for more affix characters, and false if |
| 479 * we have reached the end. | 468 * we have reached the end. |
| 480 */ | 469 */ |
| 481 bool parseCharacterAffix(StringBuffer affix) { | 470 bool parseCharacterAffix(StringBuffer affix) { |
| 482 var ch = pattern.current; | 471 var ch = pattern.current; |
| 483 if (ch == null) return false; | 472 if (ch == null) return false; |
| 484 if (ch == _QUOTE) { | 473 if (ch == _QUOTE) { |
| 485 var nextChar = pattern.peek; | 474 if (pattern.peek == _QUOTE) { |
| 486 if (nextChar == _QUOTE) { | |
| 487 pattern.moveNext(); | 475 pattern.moveNext(); |
| 488 affix.write(_QUOTE); // 'don''t' | 476 affix.write(_QUOTE); // 'don''t' |
| 489 } else { | 477 } else { |
| 490 inQuote = !inQuote; | 478 inQuote = !inQuote; |
| 491 } | 479 } |
| 492 return true; | 480 return true; |
| 493 } | 481 } |
| 494 | 482 |
| 495 if (inQuote) { | 483 if (inQuote) { |
| 496 affix.write(ch); | 484 affix.write(ch); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 547 groupingCount = -1; | 535 groupingCount = -1; |
| 548 | 536 |
| 549 var loop = true; | 537 var loop = true; |
| 550 trunk = new StringBuffer(); | 538 trunk = new StringBuffer(); |
| 551 while (pattern.current != null && loop) { | 539 while (pattern.current != null && loop) { |
| 552 loop = parseTrunkCharacter(); | 540 loop = parseTrunkCharacter(); |
| 553 } | 541 } |
| 554 | 542 |
| 555 if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { | 543 if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { |
| 556 // Handle '###.###' and '###.' and '.###' | 544 // Handle '###.###' and '###.' and '.###' |
| 557 var n = decimalPos; | 545 // Handle '.###' |
| 558 if (n == 0) { // Handle '.###' | 546 var n = decimalPos == 0 ? 1 : decimalPos; |
| 559 n++; | |
| 560 } | |
| 561 digitRightCount = digitLeftCount - n; | 547 digitRightCount = digitLeftCount - n; |
| 562 digitLeftCount = n - 1; | 548 digitLeftCount = n - 1; |
| 563 zeroDigitCount = 1; | 549 zeroDigitCount = 1; |
| 564 } | 550 } |
| 565 | 551 |
| 566 // Do syntax checking on the digits. | 552 // Do syntax checking on the digits. |
| 567 if (decimalPos < 0 && digitRightCount > 0 || | 553 if (decimalPos < 0 && digitRightCount > 0 || |
| 568 decimalPos >= 0 && (decimalPos < digitLeftCount || | 554 decimalPos >= 0 && (decimalPos < digitLeftCount || |
| 569 decimalPos > digitLeftCount + zeroDigitCount) || | 555 decimalPos > digitLeftCount + zeroDigitCount) || |
| 570 groupingCount == 0) { | 556 groupingCount == 0) { |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 695 Iterator _iterator(String s) => new _StringIterator(s); | 681 Iterator _iterator(String s) => new _StringIterator(s); |
| 696 | 682 |
| 697 // TODO(nweiz): remove this when issue 3780 is fixed. | 683 // TODO(nweiz): remove this when issue 3780 is fixed. |
| 698 /** | 684 /** |
| 699 * Provides an Iterable that wraps [_iterator] so it can be used in a `for` | 685 * Provides an Iterable that wraps [_iterator] so it can be used in a `for` |
| 700 * loop. | 686 * loop. |
| 701 */ | 687 */ |
| 702 class _StringIterable extends IterableBase<String> { | 688 class _StringIterable extends IterableBase<String> { |
| 703 final Iterator<String> iterator; | 689 final Iterator<String> iterator; |
| 704 | 690 |
| 705 _StringIterable(String s) | 691 _StringIterable(String s) : iterator = _iterator(s); |
| 706 : iterator = _iterator(s); | |
| 707 } | 692 } |
| 708 | 693 |
| 709 /** | 694 /** |
| 710 * Provides an iterator over a string as a list of substrings, and also | 695 * Provides an iterator over a string as a list of substrings, and also |
| 711 * gives us a lookahead of one via the [peek] method. | 696 * gives us a lookahead of one via the [peek] method. |
| 712 */ | 697 */ |
| 713 class _StringIterator implements Iterator<String> { | 698 class _StringIterator implements Iterator<String> { |
| 714 String input; | 699 final String input; |
| 715 var index = -1; | 700 final int length; |
| 716 inBounds(i) => i >= 0 && i < input.length; | 701 int index = -1; |
| 717 _StringIterator(this.input); | 702 inBounds(i) => i >= 0 && i < length; |
| 703 _StringIterator(String input): input = input, length = input.length; | |
|
Alan Knight
2014/01/28 01:33:46
Why move the assignment of input out of the constr
vicb
2014/01/28 07:52:45
"input" was moved out of the constructor args beca
Alan Knight
2014/01/29 01:27:58
Interesting, I didn't realize that wasn't permitte
| |
| 718 String get current => inBounds(index) ? input[index] : null; | 704 String get current => inBounds(index) ? input[index] : null; |
| 719 | 705 |
| 720 bool moveNext() => inBounds(++index); | 706 bool moveNext() => inBounds(++index); |
| 721 String get peek => inBounds(index + 1) ? input[index + 1] : null; | 707 String get peek => inBounds(index + 1) ? input[index + 1] : null; |
| 722 Iterator<String> get iterator => this; | 708 Iterator<String> get iterator => this; |
| 723 } | 709 } |
| OLD | NEW |