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

Side by Side Diff: src/extensions/experimental/number-format.cc

Issue 7129051: Adding support for number formating to the JS i18n API. (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 9 years, 6 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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "src/extensions/experimental/number-format.h"
29
30 #include <string.h>
31
32 #include "src/extensions/experimental/i18n-utils.h"
33 #include "unicode/dcfmtsym.h"
34 #include "unicode/decimfmt.h"
35 #include "unicode/locid.h"
36 #include "unicode/numfmt.h"
37 #include "unicode/uchar.h"
38 #include "unicode/ucurr.h"
39
40 namespace v8 {
41 namespace internal {
42
43 const int NumberFormat::kCurrencyCodeLength = 4;
44
45 v8::Persistent<v8::FunctionTemplate> NumberFormat::number_format_template_;
46 double NumberFormat::nan_ = 0.0;
47
48 static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String>,
49 v8::Handle<v8::String>,
50 v8::Handle<v8::Object>);
51 static icu::DecimalFormat* CreateFormatterFromSkeleton(
52 const icu::Locale&, const icu::UnicodeString&, UErrorCode*);
53 static icu::DecimalFormatSymbols* GetFormatSymbols(const icu::Locale&);
54 static bool GetCurrencyCode(const icu::Locale&, UChar*);
55 static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
56
57 icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
58 v8::Handle<v8::Object> obj) {
59 if (number_format_template_->HasInstance(obj)) {
60 return static_cast<icu::DecimalFormat*>(
61 obj->GetPointerFromInternalField(0));
62 }
63
64 return NULL;
65 }
66
67 void NumberFormat::DeleteNumberFormat(v8::Persistent<v8::Value> object,
68 void* param) {
69 v8::Persistent<v8::Object> persistent_object =
70 v8::Persistent<v8::Object>::Cast(object);
71
72 // First delete the hidden C++ object.
73 // Unpacking should never return NULL here. That would only happen if
74 // this method is used as the weak callback for persistent handles not
75 // pointing to a number formatter.
76 delete UnpackNumberFormat(persistent_object);
77
78 // Then dispose of the persistent handle to JS object.
79 persistent_object.Dispose();
80 }
81
82 v8::Handle<v8::Value> NumberFormat::Format(const v8::Arguments& args) {
83 v8::HandleScope handle_scope;
84
85 double value = nan_;
86 if (args.Length() == 1 && args[0]->IsNumber()) {
87 value = args[0]->NumberValue();
88 };
89
90 if (value == nan_) {
Mads Ager (chromium) 2011/06/10 07:25:30 This does not work. NaN is not equal to anything s
Nebojša Ćirić 2011/06/14 21:46:21 ICU can handle NaN properly so I've removed hacky
91 // Just return NaN on invalid input or if an actual value was NaN.
92 return v8::String::New("NaN");
93 }
94
95 icu::DecimalFormat* number_format = UnpackNumberFormat(args.Holder());
96 if (!number_format) {
97 return ThrowUnexpectedObjectError();
98 }
99
100 icu::UnicodeString result;
101 number_format->format(value, result);
102
103 return v8::String::New(
104 reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
105 }
106
107 v8::Handle<v8::Value> NumberFormat::JSNumberFormat(const v8::Arguments& args) {
108 v8::HandleScope handle_scope;
109
110 // Expect locale id, region id and settings.
111 if (args.Length() != 3 ||
112 !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsObject()) {
113 return v8::ThrowException(v8::Exception::SyntaxError(
114 v8::String::New("Locale, region and number settings are required.")));
115 }
116
117 // Set nan_ using JavaScript eval.
118 if (nan_ == 0.0) {
119 v8::TryCatch try_catch;
120 v8::Local<v8::Script> nan_script =
121 v8::Script::Compile(v8::String::New("eval('NaN')"));
122 nan_ = nan_script->Run()->NumberValue();
123 if (try_catch.HasCaught()) {
124 return try_catch.ReThrow();
125 }
126 }
127
128 icu::DecimalFormat* number_format = CreateNumberFormat(
129 args[0]->ToString(), args[1]->ToString(), args[2]->ToObject());
130
131 if (number_format_template_.IsEmpty()) {
132 v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
133
134 raw_template->SetClassName(v8::String::New("v8Locale.NumberFormat"));
135
136 // Define internal field count on instance template.
137 v8::Local<v8::ObjectTemplate> object_template =
138 raw_template->InstanceTemplate();
139
140 // Set aside internal field for icu number formatter.
141 object_template->SetInternalFieldCount(1);
142
143 // Define all of the prototype methods on prototype template.
144 v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
145 proto->Set(v8::String::New("format"),
146 v8::FunctionTemplate::New(Format));
147
148 number_format_template_ =
149 v8::Persistent<v8::FunctionTemplate>::New(raw_template);
150 }
151
152 // Create an empty object wrapper.
153 v8::Local<v8::Object> local_object =
154 number_format_template_->GetFunction()->NewInstance();
155 v8::Persistent<v8::Object> wrapper =
156 v8::Persistent<v8::Object>::New(local_object);
157
158 // Set number formatter as internal field of the resulting JS object.
159 wrapper->SetPointerInInternalField(0, number_format);
160
161 // Make object handle weak so we can delete iterator once GC kicks in.
162 wrapper.MakeWeak(NULL, DeleteNumberFormat);
163
164 return wrapper;
165 }
166
167 // Returns DecimalFormat.
168 static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String> locale,
169 v8::Handle<v8::String> region,
170 v8::Handle<v8::Object> settings) {
171 v8::HandleScope handle_scope;
172
173 v8::String::AsciiValue ascii_locale(locale);
174 icu::Locale icu_locale(*ascii_locale);
175
176 // Make formatter from skeleton.
177 icu::DecimalFormat* number_format = NULL;
178 UErrorCode status = U_ZERO_ERROR;
179 icu::UnicodeString setting;
180
181 if (I18NUtils::ExtractStringSetting(settings, "skeleton", &setting)) {
182 // TODO(cira): Use ICU skeleton once
183 // http://bugs.icu-project.org/trac/ticket/8610 is resolved.
184 number_format = CreateFormatterFromSkeleton(icu_locale, setting, &status);
185 } else if (I18NUtils::ExtractStringSetting(settings, "pattern", &setting)) {
186 number_format =
187 new icu::DecimalFormat(setting, GetFormatSymbols(icu_locale), status);
188 } else if (I18NUtils::ExtractStringSetting(settings, "style", &setting)) {
189 if (setting == UNICODE_STRING_SIMPLE("currency")) {
190 number_format = static_cast<icu::DecimalFormat*>(
191 icu::NumberFormat::createCurrencyInstance(icu_locale, status));
192 } else if (setting == UNICODE_STRING_SIMPLE("percent")) {
193 number_format = static_cast<icu::DecimalFormat*>(
194 icu::NumberFormat::createPercentInstance(icu_locale, status));
195 } else if (setting == UNICODE_STRING_SIMPLE("scientific")) {
196 number_format = static_cast<icu::DecimalFormat*>(
197 icu::NumberFormat::createScientificInstance(icu_locale, status));
198 } else {
199 // Make it decimal in any other case.
200 number_format = static_cast<icu::DecimalFormat*>(
201 icu::NumberFormat::createInstance(icu_locale, status));
202 }
203 }
204
205 if (U_FAILURE(status)) {
206 delete number_format;
207 status = U_ZERO_ERROR;
208 number_format = static_cast<icu::DecimalFormat*>(
209 icu::NumberFormat::createInstance(icu_locale, status));
210 }
211
212 // Attach appropriate currency code to the formatter.
213 // It affects currency formatters only.
214 v8::String::AsciiValue ascii_region(region);
215 icu::Locale icu_region(*ascii_region);
216
217 UChar currency_code[NumberFormat::kCurrencyCodeLength];
218 if (GetCurrencyCode(icu_region, currency_code)) {
219 number_format->setCurrency(currency_code, status);
220 }
221
222 return number_format;
223 }
224
225 // Generates ICU number format pattern from given skeleton.
226 static icu::DecimalFormat* CreateFormatterFromSkeleton(
227 const icu::Locale& icu_locale,
228 const icu::UnicodeString& skeleton,
229 UErrorCode* status) {
230 icu::DecimalFormat skeleton_format(
231 skeleton, GetFormatSymbols(icu_locale), *status);
232
233 // Find out if skeleton contains currency or percent symbol and create
234 // proper instance to tweak.
235 icu::DecimalFormat* base_format = NULL;
236
237 // UChar representation of \x00A4 currency symbol.
238 static const UChar currency_symbol[] = { 0xA4, 0 };
239
240 if (skeleton.indexOf(currency_symbol) != -1) {
241 base_format = static_cast<icu::DecimalFormat*>(
242 icu::NumberFormat::createCurrencyInstance(icu_locale, *status));
243 } else if (skeleton.indexOf('%') != -1) {
244 base_format = static_cast<icu::DecimalFormat*>(
245 icu::NumberFormat::createPercentInstance(icu_locale, *status));
246 } else {
247 base_format = static_cast<icu::DecimalFormat*>(
248 icu::NumberFormat::createInstance(icu_locale, *status));
249 }
250
251 if (U_FAILURE(*status)) {
252 delete base_format;
253 return NULL;
254 }
255
256 // Copy important information from skeleton to the new formatter.
257 base_format->setGroupingUsed(skeleton_format.isGroupingUsed());
258
259 base_format->setMinimumIntegerDigits(
260 skeleton_format.getMinimumIntegerDigits());
261
262 base_format->setMinimumFractionDigits(
263 skeleton_format.getMinimumFractionDigits());
264
265 base_format->setMaximumFractionDigits(
266 skeleton_format.getMaximumFractionDigits());
267
268 return base_format;
269 }
270
271 // Gets decimal symbols for a locale.
272 static icu::DecimalFormatSymbols* GetFormatSymbols(
273 const icu::Locale& icu_locale) {
274 UErrorCode status = U_ZERO_ERROR;
275 icu::DecimalFormatSymbols* symbols =
276 new icu::DecimalFormatSymbols(icu_locale, status);
277
278 if (U_FAILURE(status)) {
279 delete symbols;
280 // Use symbols from default locale.
281 symbols = new icu::DecimalFormatSymbols(status);
282 }
283
284 return symbols;
285 }
286
287 // Gets currency ISO 4217 3-letter code for a given locale ID.
288 // Returns false in case of error.
289 static bool GetCurrencyCode(const icu::Locale& icu_locale, UChar* code) {
290 const char* region = icu_locale.getName();
291
292 UErrorCode status = U_ZERO_ERROR;
293 ucurr_forLocale(region, code, NumberFormat::kCurrencyCodeLength, &status);
294 if (U_FAILURE(status)) {
295 return false;
296 }
297
298 return true;
299 }
300
301 // Throws a JavaScript exception.
302 static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
303 // Returns undefined, and schedules an exception to be thrown.
304 return v8::ThrowException(v8::Exception::Error(
305 v8::String::New("NumberFormat method called on an object "
306 "that is not a NumberFormat.")));
307 }
308
309 } } // namespace v8::internal
OLDNEW
« src/extensions/experimental/number-format.h ('K') | « src/extensions/experimental/number-format.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698