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

Side by Side Diff: pkg/i18n/number_format.dart

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

Powered by Google App Engine
This is Rietveld 408576698