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

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

Issue 9016034: Removing experimental i18n code from v8 repository. Internationalization support is hosted under ... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 9 years 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 | « src/extensions/experimental/number-format.h ('k') | no next file » | 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 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 #include "unicode/unum.h"
40 #include "unicode/uversion.h"
41
42 namespace v8 {
43 namespace internal {
44
45 const int NumberFormat::kCurrencyCodeLength = 4;
46
47 v8::Persistent<v8::FunctionTemplate> NumberFormat::number_format_template_;
48
49 static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String>,
50 v8::Handle<v8::String>,
51 v8::Handle<v8::Object>);
52 static icu::DecimalFormat* CreateFormatterFromSkeleton(
53 const icu::Locale&, const icu::UnicodeString&, UErrorCode*);
54 static icu::DecimalFormatSymbols* GetFormatSymbols(const icu::Locale&);
55 static bool GetCurrencyCode(const icu::Locale&,
56 const char* const,
57 v8::Handle<v8::Object>,
58 UChar*);
59 static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
60
61 icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
62 v8::Handle<v8::Object> obj) {
63 if (number_format_template_->HasInstance(obj)) {
64 return static_cast<icu::DecimalFormat*>(
65 obj->GetPointerFromInternalField(0));
66 }
67
68 return NULL;
69 }
70
71 void NumberFormat::DeleteNumberFormat(v8::Persistent<v8::Value> object,
72 void* param) {
73 v8::Persistent<v8::Object> persistent_object =
74 v8::Persistent<v8::Object>::Cast(object);
75
76 // First delete the hidden C++ object.
77 // Unpacking should never return NULL here. That would only happen if
78 // this method is used as the weak callback for persistent handles not
79 // pointing to a number formatter.
80 delete UnpackNumberFormat(persistent_object);
81
82 // Then dispose of the persistent handle to JS object.
83 persistent_object.Dispose();
84 }
85
86 v8::Handle<v8::Value> NumberFormat::Format(const v8::Arguments& args) {
87 v8::HandleScope handle_scope;
88
89 if (args.Length() != 1 || !args[0]->IsNumber()) {
90 // Just return NaN on invalid input.
91 return v8::String::New("NaN");
92 }
93
94 icu::DecimalFormat* number_format = UnpackNumberFormat(args.Holder());
95 if (!number_format) {
96 return ThrowUnexpectedObjectError();
97 }
98
99 // ICU will handle actual NaN value properly and return NaN string.
100 icu::UnicodeString result;
101 number_format->format(args[0]->NumberValue(), 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 icu::DecimalFormat* number_format = CreateNumberFormat(
118 args[0]->ToString(), args[1]->ToString(), args[2]->ToObject());
119
120 if (number_format_template_.IsEmpty()) {
121 v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
122
123 raw_template->SetClassName(v8::String::New("v8Locale.NumberFormat"));
124
125 // Define internal field count on instance template.
126 v8::Local<v8::ObjectTemplate> object_template =
127 raw_template->InstanceTemplate();
128
129 // Set aside internal field for icu number formatter.
130 object_template->SetInternalFieldCount(1);
131
132 // Define all of the prototype methods on prototype template.
133 v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
134 proto->Set(v8::String::New("format"),
135 v8::FunctionTemplate::New(Format));
136
137 number_format_template_ =
138 v8::Persistent<v8::FunctionTemplate>::New(raw_template);
139 }
140
141 // Create an empty object wrapper.
142 v8::Local<v8::Object> local_object =
143 number_format_template_->GetFunction()->NewInstance();
144 v8::Persistent<v8::Object> wrapper =
145 v8::Persistent<v8::Object>::New(local_object);
146
147 // Set number formatter as internal field of the resulting JS object.
148 wrapper->SetPointerInInternalField(0, number_format);
149
150 // Create options key.
151 v8::Local<v8::Object> options = v8::Object::New();
152
153 // Show what ICU decided to use for easier problem tracking.
154 // Keep it as v8 specific extension.
155 icu::UnicodeString pattern;
156 number_format->toPattern(pattern);
157 options->Set(v8::String::New("v8ResolvedPattern"),
158 v8::String::New(reinterpret_cast<const uint16_t*>(
159 pattern.getBuffer()), pattern.length()));
160
161 // Set resolved currency code in options.currency if not empty.
162 icu::UnicodeString currency(number_format->getCurrency());
163 if (!currency.isEmpty()) {
164 options->Set(v8::String::New("currencyCode"),
165 v8::String::New(reinterpret_cast<const uint16_t*>(
166 currency.getBuffer()), currency.length()));
167 }
168
169 wrapper->Set(v8::String::New("options"), options);
170
171 // Make object handle weak so we can delete iterator once GC kicks in.
172 wrapper.MakeWeak(NULL, DeleteNumberFormat);
173
174 return wrapper;
175 }
176
177 // Returns DecimalFormat.
178 static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String> locale,
179 v8::Handle<v8::String> region,
180 v8::Handle<v8::Object> settings) {
181 v8::HandleScope handle_scope;
182
183 v8::String::AsciiValue ascii_locale(locale);
184 icu::Locale icu_locale(*ascii_locale);
185
186 // Make formatter from skeleton.
187 icu::DecimalFormat* number_format = NULL;
188 UErrorCode status = U_ZERO_ERROR;
189 icu::UnicodeString setting;
190
191 if (I18NUtils::ExtractStringSetting(settings, "skeleton", &setting)) {
192 // TODO(cira): Use ICU skeleton once
193 // http://bugs.icu-project.org/trac/ticket/8610 is resolved.
194 number_format = CreateFormatterFromSkeleton(icu_locale, setting, &status);
195 } else if (I18NUtils::ExtractStringSetting(settings, "pattern", &setting)) {
196 number_format =
197 new icu::DecimalFormat(setting, GetFormatSymbols(icu_locale), status);
198 } else if (I18NUtils::ExtractStringSetting(settings, "style", &setting)) {
199 if (setting == UNICODE_STRING_SIMPLE("currency")) {
200 number_format = static_cast<icu::DecimalFormat*>(
201 icu::NumberFormat::createCurrencyInstance(icu_locale, status));
202 } else if (setting == UNICODE_STRING_SIMPLE("percent")) {
203 number_format = static_cast<icu::DecimalFormat*>(
204 icu::NumberFormat::createPercentInstance(icu_locale, status));
205 } else if (setting == UNICODE_STRING_SIMPLE("scientific")) {
206 number_format = static_cast<icu::DecimalFormat*>(
207 icu::NumberFormat::createScientificInstance(icu_locale, status));
208 } else {
209 // Make it decimal in any other case.
210 number_format = static_cast<icu::DecimalFormat*>(
211 icu::NumberFormat::createInstance(icu_locale, status));
212 }
213 }
214
215 if (U_FAILURE(status)) {
216 delete number_format;
217 status = U_ZERO_ERROR;
218 number_format = static_cast<icu::DecimalFormat*>(
219 icu::NumberFormat::createInstance(icu_locale, status));
220 }
221
222 // Attach appropriate currency code to the formatter.
223 // It affects currency formatters only.
224 // Region is full language identifier in form 'und_' + region id.
225 v8::String::AsciiValue ascii_region(region);
226
227 UChar currency_code[NumberFormat::kCurrencyCodeLength];
228 if (GetCurrencyCode(icu_locale, *ascii_region, settings, currency_code)) {
229 number_format->setCurrency(currency_code, status);
230 }
231
232 return number_format;
233 }
234
235 // Generates ICU number format pattern from given skeleton.
236 // TODO(cira): Remove once ICU includes equivalent method
237 // (see http://bugs.icu-project.org/trac/ticket/8610).
238 static icu::DecimalFormat* CreateFormatterFromSkeleton(
239 const icu::Locale& icu_locale,
240 const icu::UnicodeString& skeleton,
241 UErrorCode* status) {
242 icu::DecimalFormat skeleton_format(
243 skeleton, GetFormatSymbols(icu_locale), *status);
244
245 // Find out if skeleton contains currency or percent symbol and create
246 // proper instance to tweak.
247 icu::DecimalFormat* base_format = NULL;
248
249 // UChar representation of U+00A4 currency symbol.
250 const UChar currency_symbol = 0xA4u;
251
252 int32_t index = skeleton.indexOf(currency_symbol);
253 if (index != -1) {
254 // Find how many U+00A4 are there. There is at least one.
255 // Case of non-consecutive U+00A4 is taken care of in i18n.js.
256 int32_t end_index = skeleton.lastIndexOf(currency_symbol, index);
257
258 #if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
259 icu::NumberFormat::EStyles style;
260 switch (end_index - index) {
261 case 0:
262 style = icu::NumberFormat::kCurrencyStyle;
263 break;
264 case 1:
265 style = icu::NumberFormat::kIsoCurrencyStyle;
266 break;
267 default:
268 style = icu::NumberFormat::kPluralCurrencyStyle;
269 }
270 #else // ICU version is 4.8 or above (we ignore versions below 4.0).
271 UNumberFormatStyle style;
272 switch (end_index - index) {
273 case 0:
274 style = UNUM_CURRENCY;
275 break;
276 case 1:
277 style = UNUM_CURRENCY_ISO;
278 break;
279 default:
280 style = UNUM_CURRENCY_PLURAL;
281 }
282 #endif
283
284 base_format = static_cast<icu::DecimalFormat*>(
285 icu::NumberFormat::createInstance(icu_locale, style, *status));
286 } else if (skeleton.indexOf('%') != -1) {
287 base_format = static_cast<icu::DecimalFormat*>(
288 icu::NumberFormat::createPercentInstance(icu_locale, *status));
289 } else {
290 // TODO(cira): Handle scientific skeleton.
291 base_format = static_cast<icu::DecimalFormat*>(
292 icu::NumberFormat::createInstance(icu_locale, *status));
293 }
294
295 if (U_FAILURE(*status)) {
296 delete base_format;
297 return NULL;
298 }
299
300 // Copy important information from skeleton to the new formatter.
301 // TODO(cira): copy rounding information from skeleton?
302 base_format->setGroupingUsed(skeleton_format.isGroupingUsed());
303
304 base_format->setMinimumIntegerDigits(
305 skeleton_format.getMinimumIntegerDigits());
306
307 base_format->setMinimumFractionDigits(
308 skeleton_format.getMinimumFractionDigits());
309
310 base_format->setMaximumFractionDigits(
311 skeleton_format.getMaximumFractionDigits());
312
313 return base_format;
314 }
315
316 // Gets decimal symbols for a locale.
317 static icu::DecimalFormatSymbols* GetFormatSymbols(
318 const icu::Locale& icu_locale) {
319 UErrorCode status = U_ZERO_ERROR;
320 icu::DecimalFormatSymbols* symbols =
321 new icu::DecimalFormatSymbols(icu_locale, status);
322
323 if (U_FAILURE(status)) {
324 delete symbols;
325 // Use symbols from default locale.
326 symbols = new icu::DecimalFormatSymbols(status);
327 }
328
329 return symbols;
330 }
331
332 // Gets currency ISO 4217 3-letter code.
333 // Check currencyCode setting first, then @currency=code and in the end
334 // try to infer currency code from locale in the form 'und_' + region id.
335 // Returns false in case of error.
336 static bool GetCurrencyCode(const icu::Locale& icu_locale,
337 const char* const und_region_locale,
338 v8::Handle<v8::Object> settings,
339 UChar* code) {
340 UErrorCode status = U_ZERO_ERROR;
341
342 // If there is user specified currency code, use it.
343 icu::UnicodeString currency;
344 if (I18NUtils::ExtractStringSetting(settings, "currencyCode", &currency)) {
345 currency.extract(code, NumberFormat::kCurrencyCodeLength, status);
346 return true;
347 }
348
349 // If ICU locale has -cu- currency code use it.
350 char currency_code[NumberFormat::kCurrencyCodeLength];
351 int32_t length = icu_locale.getKeywordValue(
352 "currency", currency_code, NumberFormat::kCurrencyCodeLength, status);
353 if (length != 0) {
354 I18NUtils::AsciiToUChar(currency_code, length + 1,
355 code, NumberFormat::kCurrencyCodeLength);
356 return true;
357 }
358
359 // Otherwise infer currency code from the region id.
360 ucurr_forLocale(
361 und_region_locale, code, NumberFormat::kCurrencyCodeLength, &status);
362
363 return !!U_SUCCESS(status);
364 }
365
366 // Throws a JavaScript exception.
367 static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
368 // Returns undefined, and schedules an exception to be thrown.
369 return v8::ThrowException(v8::Exception::Error(
370 v8::String::New("NumberFormat method called on an object "
371 "that is not a NumberFormat.")));
372 }
373
374 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/extensions/experimental/number-format.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698