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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/i18n/number_symbols.dart » ('j') | pkg/i18n/number_symbols.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 #library("number_format");
Emily Fortuna 2012/08/15 21:38:16 nit: space between library and imports section?
Alan Knight 2012/08/15 23:57:44 Done.
6 #import("intl.dart");
7 #import("number_symbols.dart");
8 #import("number_symbols_data.dart");
9
10 class NumberFormat {
11
Emily Fortuna 2012/08/15 21:38:16 remove extra newline
Alan Knight 2012/08/15 23:57:44 Done.
12 /**
13 * Create a number format that prints in [newPattern] as it applies in
14 * [locale].
15 */
16 NumberFormat([String newPattern, String locale]) {
Emily Fortuna 2012/08/15 21:38:16 Can you add a TODO around here reminding us this A
Alan Knight 2012/08/15 23:57:44 Done.
17 _locale = Intl.verifiedLocale(locale);
18 _setPattern(newPattern);
19 }
20
21 /** Variables to determine how number printing behaves. */
22 String _negativePrefix = '-';
Emily Fortuna 2012/08/15 21:38:16 put fields above the constructor.
Alan Knight 2012/08/15 23:57:44 Done.
23 String _positivePrefix = '';
24 String _negativeSuffix = '';
25 String _positiveSuffix = '';
26 num _multiplier = 1;
27 num _groupingSize = 3;
Emily Fortuna 2012/08/15 21:38:16 what's this one mean? add a comment?
Alan Knight 2012/08/15 23:57:44 Done.
28 bool _decimalSeparatorAlwaysShown = false;
29 bool _useExponentialNotation = false;
30 int _maximumIntegerDigits = 40;
Emily Fortuna 2012/08/15 21:38:16 how are these determined?
Alan Knight 2012/08/15 23:57:44 These are hard-coded versions appropriate for an e
31 int _minimumIntegerDigits = 1;
Emily Fortuna 2012/08/15 21:38:16 should these be final?
Alan Knight 2012/08/15 23:57:44 They will depend on the locale. At least with the
Emily Fortuna 2012/08/16 00:10:24 What about declaring the variable as final and ini
Alan Knight 2012/08/16 16:41:49 Hmm. I didn't think it was, but now it looks like
32 int _maximumFractionDigits = 3; // invariant, >= minFractionDigits
33 int _minimumFractionDigits = 0;
34 int _minimumExponentDigits = 0;
35 bool _useSignForPositiveExponent = false;
36
37 /** The locale in which we print numbers. */
38 String _locale;
39
40 /** Caches the symbols used for our locale. */
41 NumberSymbols _symbols;
42
43 /**
44 * Transient internal state in which to build up the result of the format
45 * operation. We can have this be just an instance variable because Dart is
Emily Fortuna 2012/08/15 21:38:16 indentation
Alan Knight 2012/08/15 23:57:44 Done.
46 * single-threaded and unless we do an asynchronous operation in the process
47 * of formatting then there will only ever be one number being formatted
48 * at a time. In languages with threads we'd need to pass this on the stack.
Emily Fortuna 2012/08/15 21:38:16 Is this comment relevant? We don't have threads...
Alan Knight 2012/08/15 23:57:44 I'm saying we can do this precisely because we don
Emily Fortuna 2012/08/16 00:10:24 Gotcha. I wasn't reading this carefully.
49 */
50 StringBuffer _buffer;
51
52 /**
53 * Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
54 */
55 String get locale() => _locale;
56
57 /**
58 * Return the symbols which are used in our locale. Cache them to avoid
59 * repeated lookup.
60 */
61 NumberSymbols get symbols() {
62 if (_symbols == null) _symbols = numberFormatSymbols[locale];
63 return _symbols;
64 }
65
66 // TODO(alanknight): Actually use the pattern and locale.
67 _setPattern(x) {}
Emily Fortuna 2012/08/15 21:38:16 type for arg x?
Alan Knight 2012/08/15 23:57:44 Done.
68
69 /**
70 * Format [number] according to our pattern and return the formatted string.
71 */
72 String format(num number) {
73 if (number.isNaN()) return symbols.NAN;
74 if (number.isInfinite()) return "${_signPrefix(number)}${symbols.INFINITY}";
75
76 _newBuffer();
77 _add(_signPrefix(number));
78 _formatNumber(number.abs());
79 _add(_signSuffix(number));
80
81 var result = _buffer.toString();
82 _buffer = null;
83 return result;
84 }
85
86 /**
87 * Format the main part of the number in the form dictated by the pattern.
88 */
89 void _formatNumber(num number) {
90 if (_useExponentialNotation) {
91 _formatExponential(number);
Emily Fortuna 2012/08/15 21:38:16 indentation :-(
Alan Knight 2012/08/15 23:57:44 Done.
92 } else {
93 _formatFixed(number);
94 }
95 }
96
97 /** Format the number in exponential notation. */
98 _formatExponential(num number) {
99 if (number == 0.0) {
100 _formatFixed(number);
101 _formatExponent(0);
102 return;
103 }
104
105 var exponent = (Math.log(number) / Math.log(10)).floor();
106 var mantissa = number / Math.pow(10, exponent);
107
108 if (_minimumIntegerDigits < 1) {
109 exponent++;
110 mantissa /= 10;
111 } else {
112 exponent -= _minimumIntegerDigits - 1;
113 mantissa *= Math.pow(10, _minimumIntegerDigits - 1);
114 }
115 _formatFixed(number);
116 _formatExponent(exponent);
117 }
118
119 /**
120 * Format the exponent portion, e.g. in "1.3e-5" the "e-5".
121 */
122 void _formatExponent(num exponent) {
123 _add(symbols.EXP_SYMBOL);
124 if (exponent < 0) {
125 exponent = -exponent;
126 _add(symbols.MINUS_SIGN);
127 } else if (_useSignForPositiveExponent) {
128 _add(symbols.PLUS_SIGN);
129 }
130 _pad(_minimumExponentDigits, exponent.toString());
131 }
132
133 /**
134 * Format the basic number portion, inluding the digits after the decimal
Emily Fortuna 2012/08/15 21:38:16 "digits after the decimal point" --> "fractional d
Alan Knight 2012/08/15 23:57:44 Done.
135 * point.
136 */
137 void _formatFixed(num number) {
138 // round the number
Emily Fortuna 2012/08/15 21:38:16 Comments start with a capital letter and end in a
Alan Knight 2012/08/15 23:57:44 Done.
139 var power = Math.pow(10, _maximumFractionDigits);
140 var intValue = number.truncate().toInt();
141 var multiplied = (number * power).round();
142 var fracValue = (multiplied - intValue * power).floor().toInt();
143
Emily Fortuna 2012/08/15 21:38:16 is this an extra newline here?
Alan Knight 2012/08/15 23:57:44 Done.
144 var fractionPresent = _minimumFractionDigits > 0 || fracValue > 0;
145
146 // On dartj2s the integer part may be large enough to be a floating
Emily Fortuna 2012/08/15 21:38:16 but we just populated intValue = number.truncate()
Alan Knight 2012/08/15 23:57:44 Sadly, that's not true on dart2js. Like, seriously
147 // point value, in which case we reduce it until it is small enough
148 // to be printed as an integer and pad the remainder with zeros.
149 var paddingDigits = new StringBuffer();
150 while (intValue is! int) {
151 paddingDigits.add(symbols.ZERO_DIGIT);
152 intValue = (intValue / 10).toInt();
153 }
154 var integerDigits = "${intValue}${paddingDigits}".charCodes();
155 var digitLength = integerDigits.length;
156
157 if (_hasPrintableIntegerPart(intValue)) {
158 _pad(_minimumIntegerDigits - digitLength);
159 for (var i = 0; i < digitLength; i++) {
160 _addDigit(integerDigits[i]);
Emily Fortuna 2012/08/15 21:38:16 question -- are numbers always RTL, or are there s
Alan Knight 2012/08/15 23:57:44 Good question. Added a TODO. At the moment this mi
161 _group(digitLength, i);
162 }
163 } else if (!fractionPresent) {
164 // If neither fraction nor integer part exists, just print zero.
165 _addZero();
166 }
167
168 _decimalSeparator(fractionPresent);
169 _formatFractionPart((fracValue + power).toString());
170 }
171
172 /**
173 * Format the part after the decimal place in a fixed point number.
174 */
175 void _formatFractionPart(String fractionPart) {
176 var fractionCodes = fractionPart.charCodes();
177 var fractionLength = fractionPart.length;
178 while (fractionPart[fractionLength - 1] == '0' &&
179 fractionLength > _minimumFractionDigits + 1) {
180 fractionLength--;
181 }
182 for (var i = 1; i < fractionLength; i++) {
183 _addDigit(fractionCodes[i]);
184 }
185 }
186
187 /** Print the decimal separator if appropriate. */
188 void _decimalSeparator(bool fractionPresent) {
189 if (_decimalSeparatorAlwaysShown || fractionPresent) {
190 _add(symbols.DECIMAL_SEP);
191 }
192 }
193
194 /**
195 * Return true if we have a main integer part which is printable, either
196 * because we have digits left of the decimal point, or because there are
197 * a minimum number of printable digits greater than 1.
198 */
199 bool _hasPrintableIntegerPart(int intValue) => intValue > 0 ||
Emily Fortuna 2012/08/15 21:38:16 if the function is more than one line, make it use
Alan Knight 2012/08/15 23:57:44 Done.
200 _minimumIntegerDigits > 0;
201
202 /**
203 * Create a new empty buffer. See comment on [_buffer] variable for why
204 * we have it as an instance variable rather than passing it on the stack.
205 */
206 void _newBuffer() { _buffer = new StringBuffer();}
Emily Fortuna 2012/08/15 21:38:16 space after ;
Alan Knight 2012/08/15 23:57:44 Done.
207
208 /** A group of methods that provide support for writing digits and other
209 * required characters into [_buffer] easily.
210 */
211 void _add(String x) { _buffer.add(x);}
Emily Fortuna 2012/08/15 21:38:16 space after ; , here and below
Alan Knight 2012/08/15 23:57:44 Done.
212 void _addCharCode(int x) { _buffer.addCharCode(x);}
213 void _addZero() { _buffer.add(symbols.ZERO_DIGIT);}
214 void _addDigit(int x) { _buffer.addCharCode(_localeZero + x - _zero);}
215
216 /** Print padding up to [numberOfDigits] above what's included in [basic]. */
217 void _pad(int numberOfDigits, [String basic = '']) {
218 for (var i = 0; i < numberOfDigits - basic.length; i++) {
219 _add(symbols.ZERO_DIGIT);
220 }
221 for (var x in basic.charCodes()) {
222 _addDigit(x);
223 }
224 }
225
226 /**
227 * Print the grouping symbol (e.g. comma as thousands separator) for digits
228 * left of the decimal place if it's required at this position.
229 */
230 String _group(int digitLen, int position) {
Emily Fortuna 2012/08/15 21:38:16 what do digitLen vs position actually mean?
Alan Knight 2012/08/15 23:57:44 Yes, that method was confusing. Rewrote and improv
231 if (digitLen - position > 1 && _groupingSize > 0 &&
232 ((digitLen - position) % _groupingSize == 1)) {
233 _add(symbols.GROUP_SEP);
234 }
235 }
236
237 /** Returns the code point for the character '0'. */
238 int get _zero() => '0'.charCodes()[0];
239
240 /** Returns the code point for the locale's zero digit. */
241 int get _localeZero() => symbols.ZERO_DIGIT.charCodeAt(0);
242
243 /**
244 * Returns the prefix for [x] based on wether it's positive or negative.
Emily Fortuna 2012/08/15 21:38:16 wether -> whether
Alan Knight 2012/08/15 23:57:44 Done.
245 * In en_US this would be '' and '-' respectively.
246 */
247 String _signPrefix(num x) {
248 return x.isNegative() ? _negativePrefix : _positivePrefix;
249 }
250
251 /**
252 * Returns the suffix for [x] based on wether it's positive or negative.
253 * In en_US there are no suffixes for positive or negative.
254 */
255 String _signSuffix(num x) {
256 return x.isNegative() ? _negativeSuffix : _positiveSuffix;
257 }
258 }
OLDNEW
« no previous file with comments | « no previous file | pkg/i18n/number_symbols.dart » ('j') | pkg/i18n/number_symbols.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698