Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Unified Diff: pkg/i18n/number_format.dart

Issue 10834318: First iteration on number formatting (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/i18n/number_symbols.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/i18n/number_format.dart
===================================================================
--- pkg/i18n/number_format.dart (revision 0)
+++ pkg/i18n/number_format.dart (revision 0)
@@ -0,0 +1,268 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#library("number_format");
+
+#import("intl.dart");
+#import("number_symbols.dart");
+#import("number_symbols_data.dart");
+
+class NumberFormat {
+ /** Variables to determine how number printing behaves. */
+ String _negativePrefix = '-';
+ String _positivePrefix = '';
+ String _negativeSuffix = '';
+ String _positiveSuffix = '';
+ /** How many numbers in a group when using punctuation to group digits in
+ * large numbers. e.g. in en_US: "1,000,000" has a grouping size of 3 digits
+ * between commas.
+ */
+ int _groupingSize = 3;
+ bool _decimalSeparatorAlwaysShown = false;
+ bool _useExponentialNotation = false;
+ int _maximumIntegerDigits = 40;
+ int _minimumIntegerDigits = 1;
+ int _maximumFractionDigits = 3; // invariant, >= minFractionDigits
+ int _minimumFractionDigits = 0;
+ int _minimumExponentDigits = 0;
+ bool _useSignForPositiveExponent = false;
+
+ /** The locale in which we print numbers. */
+ String _locale;
+
+ /** Caches the symbols used for our locale. */
+ NumberSymbols _symbols;
+
+ /**
+ * Transient internal state in which to build up the result of the format
+ * operation. We can have this be just an instance variable because Dart is
+ * single-threaded and unless we do an asynchronous operation in the process
+ * of formatting then there will only ever be one number being formatted
+ * at a time. In languages with threads we'd need to pass this on the stack.
+ */
+ StringBuffer _buffer;
+
+ /**
+ * Create a number format that prints in [newPattern] as it applies in
+ * [locale].
+ */
+ NumberFormat([String newPattern, String locale]) {
+ // TODO(alanknight): There will need to be some kind of async setup
+ // operations so as not to bring along every locale in every program.
+ _locale = Intl.verifiedLocale(locale);
+ _setPattern(newPattern);
+ }
+
+ /**
+ * Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
+ */
+ String get locale() => _locale;
+
+ /**
+ * Return the symbols which are used in our locale. Cache them to avoid
+ * repeated lookup.
+ */
+ NumberSymbols get symbols() {
+ if (_symbols == null) _symbols = numberFormatSymbols[locale];
+ return _symbols;
+ }
+
+ // TODO(alanknight): Actually use the pattern and locale.
+ _setPattern(String x) {}
+
+ /**
+ * Format [number] according to our pattern and return the formatted string.
+ */
+ String format(num number) {
+ // TODO(alanknight): Do we have to do anything for printing numbers bidi?
+ // Or are they always printed left to right?
+ if (number.isNaN()) return symbols.NAN;
+ if (number.isInfinite()) return "${_signPrefix(number)}${symbols.INFINITY}";
+
+ _newBuffer();
+ _add(_signPrefix(number));
+ _formatNumber(number.abs());
+ _add(_signSuffix(number));
+
+ var result = _buffer.toString();
+ _buffer = null;
+ return result;
+ }
+
+ /**
+ * Format the main part of the number in the form dictated by the pattern.
+ */
+ void _formatNumber(num number) {
+ if (_useExponentialNotation) {
+ _formatExponential(number);
+ } else {
+ _formatFixed(number);
+ }
+ }
+
+ /** Format the number in exponential notation. */
+ _formatExponential(num number) {
+ if (number == 0.0) {
+ _formatFixed(number);
+ _formatExponent(0);
+ return;
+ }
+
+ var exponent = (Math.log(number) / Math.log(10)).floor();
+ var mantissa = number / Math.pow(10, exponent);
+
+ if (_minimumIntegerDigits < 1) {
+ exponent++;
+ mantissa /= 10;
+ } else {
+ exponent -= _minimumIntegerDigits - 1;
+ mantissa *= Math.pow(10, _minimumIntegerDigits - 1);
+ }
+ _formatFixed(number);
+ _formatExponent(exponent);
+ }
+
+ /**
+ * Format the exponent portion, e.g. in "1.3e-5" the "e-5".
+ */
+ void _formatExponent(num exponent) {
+ _add(symbols.EXP_SYMBOL);
+ if (exponent < 0) {
+ exponent = -exponent;
+ _add(symbols.MINUS_SIGN);
+ } else if (_useSignForPositiveExponent) {
+ _add(symbols.PLUS_SIGN);
+ }
+ _pad(_minimumExponentDigits, exponent.toString());
+ }
+
+ /**
+ * Format the basic number portion, inluding the fractional digits.
+ */
+ void _formatFixed(num number) {
+ // Round the number.
+ var power = Math.pow(10, _maximumFractionDigits);
+ var intValue = number.truncate().toInt();
+ var multiplied = (number * power).round();
+ var fracValue = (multiplied - intValue * power).floor().toInt();
+ var fractionPresent = _minimumFractionDigits > 0 || fracValue > 0;
+
+ // On dartj2s the integer part may be large enough to be a floating
+ // point value, in which case we reduce it until it is small enough
+ // to be printed as an integer and pad the remainder with zeros.
+ var paddingDigits = new StringBuffer();
+ while (intValue is! int) {
+ paddingDigits.add(symbols.ZERO_DIGIT);
+ intValue = (intValue / 10).toInt();
+ }
+ var integerDigits = "${intValue}${paddingDigits}".charCodes();
+ var digitLength = integerDigits.length;
+
+ if (_hasPrintableIntegerPart(intValue)) {
+ _pad(_minimumIntegerDigits - digitLength);
+ for (var i = 0; i < digitLength; i++) {
+ _addDigit(integerDigits[i]);
+ _group(digitLength, i);
+ }
+ } else if (!fractionPresent) {
+ // If neither fraction nor integer part exists, just print zero.
+ _addZero();
+ }
+
+ _decimalSeparator(fractionPresent);
+ _formatFractionPart((fracValue + power).toString());
+ }
+
+ /**
+ * Format the part after the decimal place in a fixed point number.
+ */
+ void _formatFractionPart(String fractionPart) {
+ var fractionCodes = fractionPart.charCodes();
+ var fractionLength = fractionPart.length;
+ while (fractionPart[fractionLength - 1] == '0' &&
+ fractionLength > _minimumFractionDigits + 1) {
+ fractionLength--;
+ }
+ for (var i = 1; i < fractionLength; i++) {
+ _addDigit(fractionCodes[i]);
+ }
+ }
+
+ /** Print the decimal separator if appropriate. */
+ void _decimalSeparator(bool fractionPresent) {
+ if (_decimalSeparatorAlwaysShown || fractionPresent) {
+ _add(symbols.DECIMAL_SEP);
+ }
+ }
+
+ /**
+ * Return true if we have a main integer part which is printable, either
+ * because we have digits left of the decimal point, or because there are
+ * a minimum number of printable digits greater than 1.
+ */
+ bool _hasPrintableIntegerPart(int intValue) {
+ return intValue > 0 || _minimumIntegerDigits > 0;
+ }
+
+ /**
+ * Create a new empty buffer. See comment on [_buffer] variable for why
+ * we have it as an instance variable rather than passing it on the stack.
+ */
+ void _newBuffer() { _buffer = new StringBuffer(); }
+
+ /** A group of methods that provide support for writing digits and other
+ * required characters into [_buffer] easily.
+ */
+ void _add(String x) { _buffer.add(x);}
+ void _addCharCode(int x) { _buffer.addCharCode(x); }
+ void _addZero() { _buffer.add(symbols.ZERO_DIGIT); }
+ void _addDigit(int x) { _buffer.addCharCode(_localeZero + x - _zero); }
+
+ /** Print padding up to [numberOfDigits] above what's included in [basic]. */
+ void _pad(int numberOfDigits, [String basic = '']) {
+ for (var i = 0; i < numberOfDigits - basic.length; i++) {
+ _add(symbols.ZERO_DIGIT);
+ }
+ for (var x in basic.charCodes()) {
+ _addDigit(x);
+ }
+ }
+
+ /**
+ * We are printing the digits of the number from left to right. We may need
+ * to print a thousands separator or other grouping character as appropriate
+ * to the locale. So we find how many places we are from the end of the number
+ * by subtracting our current [position] from the [totalLength] and print
+ * the separator character every [_groupingSize] digits.
+ */
+ void _group(int totalLength, int position) {
+ var distanceFromEnd = totalLength - position;
+ if (distanceFromEnd <=1 || _groupingSize <= 0) return;
Emily Fortuna 2012/08/16 00:10:24 spacing: distanceFromEnd <= 1
Alan Knight 2012/08/16 16:41:50 Done.
+ if (distanceFromEnd % _groupingSize == 1) {
+ _add(symbols.GROUP_SEP);
+ }
+ }
+
+ /** Returns the code point for the character '0'. */
+ int get _zero() => '0'.charCodes()[0];
+
+ /** Returns the code point for the locale's zero digit. */
+ int get _localeZero() => symbols.ZERO_DIGIT.charCodeAt(0);
+
+ /**
+ * Returns the prefix for [x] based on whether it's positive or negative.
+ * In en_US this would be '' and '-' respectively.
+ */
+ String _signPrefix(num x) {
+ return x.isNegative() ? _negativePrefix : _positivePrefix;
+ }
+
+ /**
+ * Returns the suffix for [x] based on wether it's positive or negative.
+ * In en_US there are no suffixes for positive or negative.
+ */
+ String _signSuffix(num x) {
+ return x.isNegative() ? _negativeSuffix : _positiveSuffix;
+ }
+}
« no previous file with comments | « no previous file | pkg/i18n/number_symbols.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698