OLD | NEW |
---|---|
(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 } | |
OLD | NEW |