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

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') | no next file with comments »
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");
6
7 #import("intl.dart");
8 #import("number_symbols.dart");
9 #import("number_symbols_data.dart");
10
11 class NumberFormat {
12 /** Variables to determine how number printing behaves. */
13 String _negativePrefix = '-';
14 String _positivePrefix = '';
15 String _negativeSuffix = '';
16 String _positiveSuffix = '';
17 /** How many numbers in a group when using punctuation to group digits in
18 * large numbers. e.g. in en_US: "1,000,000" has a grouping size of 3 digits
19 * between commas.
20 */
21 int _groupingSize = 3;
22 bool _decimalSeparatorAlwaysShown = false;
23 bool _useExponentialNotation = false;
24 int _maximumIntegerDigits = 40;
25 int _minimumIntegerDigits = 1;
26 int _maximumFractionDigits = 3; // invariant, >= minFractionDigits
27 int _minimumFractionDigits = 0;
28 int _minimumExponentDigits = 0;
29 bool _useSignForPositiveExponent = false;
30
31 /** The locale in which we print numbers. */
32 String _locale;
33
34 /** Caches the symbols used for our locale. */
35 NumberSymbols _symbols;
36
37 /**
38 * Transient internal state in which to build up the result of the format
39 * operation. We can have this be just an instance variable because Dart is
40 * single-threaded and unless we do an asynchronous operation in the process
41 * of formatting then there will only ever be one number being formatted
42 * at a time. In languages with threads we'd need to pass this on the stack.
43 */
44 StringBuffer _buffer;
45
46 /**
47 * Create a number format that prints in [newPattern] as it applies in
48 * [locale].
49 */
50 NumberFormat([String newPattern, String locale]) {
51 // TODO(alanknight): There will need to be some kind of async setup
52 // operations so as not to bring along every locale in every program.
53 _locale = Intl.verifiedLocale(locale);
54 _setPattern(newPattern);
55 }
56
57 /**
58 * Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
59 */
60 String get locale() => _locale;
61
62 /**
63 * Return the symbols which are used in our locale. Cache them to avoid
64 * repeated lookup.
65 */
66 NumberSymbols get symbols() {
67 if (_symbols == null) _symbols = numberFormatSymbols[locale];
68 return _symbols;
69 }
70
71 // TODO(alanknight): Actually use the pattern and locale.
72 _setPattern(String x) {}
73
74 /**
75 * Format [number] according to our pattern and return the formatted string.
76 */
77 String format(num number) {
78 // TODO(alanknight): Do we have to do anything for printing numbers bidi?
79 // Or are they always printed left to right?
80 if (number.isNaN()) return symbols.NAN;
81 if (number.isInfinite()) return "${_signPrefix(number)}${symbols.INFINITY}";
82
83 _newBuffer();
84 _add(_signPrefix(number));
85 _formatNumber(number.abs());
86 _add(_signSuffix(number));
87
88 var result = _buffer.toString();
89 _buffer = null;
90 return result;
91 }
92
93 /**
94 * Format the main part of the number in the form dictated by the pattern.
95 */
96 void _formatNumber(num number) {
97 if (_useExponentialNotation) {
98 _formatExponential(number);
99 } else {
100 _formatFixed(number);
101 }
102 }
103
104 /** Format the number in exponential notation. */
105 _formatExponential(num number) {
106 if (number == 0.0) {
107 _formatFixed(number);
108 _formatExponent(0);
109 return;
110 }
111
112 var exponent = (Math.log(number) / Math.log(10)).floor();
113 var mantissa = number / Math.pow(10, exponent);
114
115 if (_minimumIntegerDigits < 1) {
116 exponent++;
117 mantissa /= 10;
118 } else {
119 exponent -= _minimumIntegerDigits - 1;
120 mantissa *= Math.pow(10, _minimumIntegerDigits - 1);
121 }
122 _formatFixed(number);
123 _formatExponent(exponent);
124 }
125
126 /**
127 * Format the exponent portion, e.g. in "1.3e-5" the "e-5".
128 */
129 void _formatExponent(num exponent) {
130 _add(symbols.EXP_SYMBOL);
131 if (exponent < 0) {
132 exponent = -exponent;
133 _add(symbols.MINUS_SIGN);
134 } else if (_useSignForPositiveExponent) {
135 _add(symbols.PLUS_SIGN);
136 }
137 _pad(_minimumExponentDigits, exponent.toString());
138 }
139
140 /**
141 * Format the basic number portion, inluding the fractional digits.
142 */
143 void _formatFixed(num number) {
144 // Round the number.
145 var power = Math.pow(10, _maximumFractionDigits);
146 var intValue = number.truncate().toInt();
147 var multiplied = (number * power).round();
148 var fracValue = (multiplied - intValue * power).floor().toInt();
149 var fractionPresent = _minimumFractionDigits > 0 || fracValue > 0;
150
151 // On dartj2s the integer part may be large enough to be a floating
152 // point value, in which case we reduce it until it is small enough
153 // to be printed as an integer and pad the remainder with zeros.
154 var paddingDigits = new StringBuffer();
155 while (intValue is! int) {
156 paddingDigits.add(symbols.ZERO_DIGIT);
157 intValue = (intValue / 10).toInt();
158 }
159 var integerDigits = "${intValue}${paddingDigits}".charCodes();
160 var digitLength = integerDigits.length;
161
162 if (_hasPrintableIntegerPart(intValue)) {
163 _pad(_minimumIntegerDigits - digitLength);
164 for (var i = 0; i < digitLength; i++) {
165 _addDigit(integerDigits[i]);
166 _group(digitLength, i);
167 }
168 } else if (!fractionPresent) {
169 // If neither fraction nor integer part exists, just print zero.
170 _addZero();
171 }
172
173 _decimalSeparator(fractionPresent);
174 _formatFractionPart((fracValue + power).toString());
175 }
176
177 /**
178 * Format the part after the decimal place in a fixed point number.
179 */
180 void _formatFractionPart(String fractionPart) {
181 var fractionCodes = fractionPart.charCodes();
182 var fractionLength = fractionPart.length;
183 while (fractionPart[fractionLength - 1] == '0' &&
184 fractionLength > _minimumFractionDigits + 1) {
185 fractionLength--;
186 }
187 for (var i = 1; i < fractionLength; i++) {
188 _addDigit(fractionCodes[i]);
189 }
190 }
191
192 /** Print the decimal separator if appropriate. */
193 void _decimalSeparator(bool fractionPresent) {
194 if (_decimalSeparatorAlwaysShown || fractionPresent) {
195 _add(symbols.DECIMAL_SEP);
196 }
197 }
198
199 /**
200 * Return true if we have a main integer part which is printable, either
201 * because we have digits left of the decimal point, or because there are
202 * a minimum number of printable digits greater than 1.
203 */
204 bool _hasPrintableIntegerPart(int intValue) {
205 return intValue > 0 || _minimumIntegerDigits > 0;
206 }
207
208 /**
209 * Create a new empty buffer. See comment on [_buffer] variable for why
210 * we have it as an instance variable rather than passing it on the stack.
211 */
212 void _newBuffer() { _buffer = new StringBuffer(); }
213
214 /** A group of methods that provide support for writing digits and other
215 * required characters into [_buffer] easily.
216 */
217 void _add(String x) { _buffer.add(x);}
218 void _addCharCode(int x) { _buffer.addCharCode(x); }
219 void _addZero() { _buffer.add(symbols.ZERO_DIGIT); }
220 void _addDigit(int x) { _buffer.addCharCode(_localeZero + x - _zero); }
221
222 /** Print padding up to [numberOfDigits] above what's included in [basic]. */
223 void _pad(int numberOfDigits, [String basic = '']) {
224 for (var i = 0; i < numberOfDigits - basic.length; i++) {
225 _add(symbols.ZERO_DIGIT);
226 }
227 for (var x in basic.charCodes()) {
228 _addDigit(x);
229 }
230 }
231
232 /**
233 * We are printing the digits of the number from left to right. We may need
234 * to print a thousands separator or other grouping character as appropriate
235 * to the locale. So we find how many places we are from the end of the number
236 * by subtracting our current [position] from the [totalLength] and print
237 * the separator character every [_groupingSize] digits.
238 */
239 void _group(int totalLength, int position) {
240 var distanceFromEnd = totalLength - position;
241 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.
242 if (distanceFromEnd % _groupingSize == 1) {
243 _add(symbols.GROUP_SEP);
244 }
245 }
246
247 /** Returns the code point for the character '0'. */
248 int get _zero() => '0'.charCodes()[0];
249
250 /** Returns the code point for the locale's zero digit. */
251 int get _localeZero() => symbols.ZERO_DIGIT.charCodeAt(0);
252
253 /**
254 * Returns the prefix for [x] based on whether it's positive or negative.
255 * In en_US this would be '' and '-' respectively.
256 */
257 String _signPrefix(num x) {
258 return x.isNegative() ? _negativePrefix : _positivePrefix;
259 }
260
261 /**
262 * Returns the suffix for [x] based on wether it's positive or negative.
263 * In en_US there are no suffixes for positive or negative.
264 */
265 String _signSuffix(num x) {
266 return x.isNegative() ? _negativeSuffix : _positiveSuffix;
267 }
268 }
OLDNEW
« 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