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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: src/extensions/experimental/number-format.cc
===================================================================
--- src/extensions/experimental/number-format.cc (revision 0)
+++ src/extensions/experimental/number-format.cc (revision 0)
@@ -0,0 +1,309 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/extensions/experimental/number-format.h"
+
+#include <string.h>
+
+#include "src/extensions/experimental/i18n-utils.h"
+#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.h"
+#include "unicode/locid.h"
+#include "unicode/numfmt.h"
+#include "unicode/uchar.h"
+#include "unicode/ucurr.h"
+
+namespace v8 {
+namespace internal {
+
+const int NumberFormat::kCurrencyCodeLength = 4;
+
+v8::Persistent<v8::FunctionTemplate> NumberFormat::number_format_template_;
+double NumberFormat::nan_ = 0.0;
+
+static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String>,
+ v8::Handle<v8::String>,
+ v8::Handle<v8::Object>);
+static icu::DecimalFormat* CreateFormatterFromSkeleton(
+ const icu::Locale&, const icu::UnicodeString&, UErrorCode*);
+static icu::DecimalFormatSymbols* GetFormatSymbols(const icu::Locale&);
+static bool GetCurrencyCode(const icu::Locale&, UChar*);
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
+
+icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
+ v8::Handle<v8::Object> obj) {
+ if (number_format_template_->HasInstance(obj)) {
+ return static_cast<icu::DecimalFormat*>(
+ obj->GetPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void NumberFormat::DeleteNumberFormat(v8::Persistent<v8::Value> object,
+ void* param) {
+ v8::Persistent<v8::Object> persistent_object =
+ v8::Persistent<v8::Object>::Cast(object);
+
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a number formatter.
+ delete UnpackNumberFormat(persistent_object);
+
+ // Then dispose of the persistent handle to JS object.
+ persistent_object.Dispose();
+}
+
+v8::Handle<v8::Value> NumberFormat::Format(const v8::Arguments& args) {
+ v8::HandleScope handle_scope;
+
+ double value = nan_;
+ if (args.Length() == 1 && args[0]->IsNumber()) {
+ value = args[0]->NumberValue();
+ };
+
+ 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
+ // Just return NaN on invalid input or if an actual value was NaN.
+ return v8::String::New("NaN");
+ }
+
+ icu::DecimalFormat* number_format = UnpackNumberFormat(args.Holder());
+ if (!number_format) {
+ return ThrowUnexpectedObjectError();
+ }
+
+ icu::UnicodeString result;
+ number_format->format(value, result);
+
+ return v8::String::New(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
+}
+
+v8::Handle<v8::Value> NumberFormat::JSNumberFormat(const v8::Arguments& args) {
+ v8::HandleScope handle_scope;
+
+ // Expect locale id, region id and settings.
+ if (args.Length() != 3 ||
+ !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsObject()) {
+ return v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Locale, region and number settings are required.")));
+ }
+
+ // Set nan_ using JavaScript eval.
+ if (nan_ == 0.0) {
+ v8::TryCatch try_catch;
+ v8::Local<v8::Script> nan_script =
+ v8::Script::Compile(v8::String::New("eval('NaN')"));
+ nan_ = nan_script->Run()->NumberValue();
+ if (try_catch.HasCaught()) {
+ return try_catch.ReThrow();
+ }
+ }
+
+ icu::DecimalFormat* number_format = CreateNumberFormat(
+ args[0]->ToString(), args[1]->ToString(), args[2]->ToObject());
+
+ if (number_format_template_.IsEmpty()) {
+ v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
+
+ raw_template->SetClassName(v8::String::New("v8Locale.NumberFormat"));
+
+ // Define internal field count on instance template.
+ v8::Local<v8::ObjectTemplate> object_template =
+ raw_template->InstanceTemplate();
+
+ // Set aside internal field for icu number formatter.
+ object_template->SetInternalFieldCount(1);
+
+ // Define all of the prototype methods on prototype template.
+ v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
+ proto->Set(v8::String::New("format"),
+ v8::FunctionTemplate::New(Format));
+
+ number_format_template_ =
+ v8::Persistent<v8::FunctionTemplate>::New(raw_template);
+ }
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object =
+ number_format_template_->GetFunction()->NewInstance();
+ v8::Persistent<v8::Object> wrapper =
+ v8::Persistent<v8::Object>::New(local_object);
+
+ // Set number formatter as internal field of the resulting JS object.
+ wrapper->SetPointerInInternalField(0, number_format);
+
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak(NULL, DeleteNumberFormat);
+
+ return wrapper;
+}
+
+// Returns DecimalFormat.
+static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String> locale,
+ v8::Handle<v8::String> region,
+ v8::Handle<v8::Object> settings) {
+ v8::HandleScope handle_scope;
+
+ v8::String::AsciiValue ascii_locale(locale);
+ icu::Locale icu_locale(*ascii_locale);
+
+ // Make formatter from skeleton.
+ icu::DecimalFormat* number_format = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ icu::UnicodeString setting;
+
+ if (I18NUtils::ExtractStringSetting(settings, "skeleton", &setting)) {
+ // TODO(cira): Use ICU skeleton once
+ // http://bugs.icu-project.org/trac/ticket/8610 is resolved.
+ number_format = CreateFormatterFromSkeleton(icu_locale, setting, &status);
+ } else if (I18NUtils::ExtractStringSetting(settings, "pattern", &setting)) {
+ number_format =
+ new icu::DecimalFormat(setting, GetFormatSymbols(icu_locale), status);
+ } else if (I18NUtils::ExtractStringSetting(settings, "style", &setting)) {
+ if (setting == UNICODE_STRING_SIMPLE("currency")) {
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createCurrencyInstance(icu_locale, status));
+ } else if (setting == UNICODE_STRING_SIMPLE("percent")) {
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createPercentInstance(icu_locale, status));
+ } else if (setting == UNICODE_STRING_SIMPLE("scientific")) {
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createScientificInstance(icu_locale, status));
+ } else {
+ // Make it decimal in any other case.
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, status));
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ delete number_format;
+ status = U_ZERO_ERROR;
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, status));
+ }
+
+ // Attach appropriate currency code to the formatter.
+ // It affects currency formatters only.
+ v8::String::AsciiValue ascii_region(region);
+ icu::Locale icu_region(*ascii_region);
+
+ UChar currency_code[NumberFormat::kCurrencyCodeLength];
+ if (GetCurrencyCode(icu_region, currency_code)) {
+ number_format->setCurrency(currency_code, status);
+ }
+
+ return number_format;
+}
+
+// Generates ICU number format pattern from given skeleton.
+static icu::DecimalFormat* CreateFormatterFromSkeleton(
+ const icu::Locale& icu_locale,
+ const icu::UnicodeString& skeleton,
+ UErrorCode* status) {
+ icu::DecimalFormat skeleton_format(
+ skeleton, GetFormatSymbols(icu_locale), *status);
+
+ // Find out if skeleton contains currency or percent symbol and create
+ // proper instance to tweak.
+ icu::DecimalFormat* base_format = NULL;
+
+ // UChar representation of \x00A4 currency symbol.
+ static const UChar currency_symbol[] = { 0xA4, 0 };
+
+ if (skeleton.indexOf(currency_symbol) != -1) {
+ base_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createCurrencyInstance(icu_locale, *status));
+ } else if (skeleton.indexOf('%') != -1) {
+ base_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createPercentInstance(icu_locale, *status));
+ } else {
+ base_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, *status));
+ }
+
+ if (U_FAILURE(*status)) {
+ delete base_format;
+ return NULL;
+ }
+
+ // Copy important information from skeleton to the new formatter.
+ base_format->setGroupingUsed(skeleton_format.isGroupingUsed());
+
+ base_format->setMinimumIntegerDigits(
+ skeleton_format.getMinimumIntegerDigits());
+
+ base_format->setMinimumFractionDigits(
+ skeleton_format.getMinimumFractionDigits());
+
+ base_format->setMaximumFractionDigits(
+ skeleton_format.getMaximumFractionDigits());
+
+ return base_format;
+}
+
+// Gets decimal symbols for a locale.
+static icu::DecimalFormatSymbols* GetFormatSymbols(
+ const icu::Locale& icu_locale) {
+ UErrorCode status = U_ZERO_ERROR;
+ icu::DecimalFormatSymbols* symbols =
+ new icu::DecimalFormatSymbols(icu_locale, status);
+
+ if (U_FAILURE(status)) {
+ delete symbols;
+ // Use symbols from default locale.
+ symbols = new icu::DecimalFormatSymbols(status);
+ }
+
+ return symbols;
+}
+
+// Gets currency ISO 4217 3-letter code for a given locale ID.
+// Returns false in case of error.
+static bool GetCurrencyCode(const icu::Locale& icu_locale, UChar* code) {
+ const char* region = icu_locale.getName();
+
+ UErrorCode status = U_ZERO_ERROR;
+ ucurr_forLocale(region, code, NumberFormat::kCurrencyCodeLength, &status);
+ if (U_FAILURE(status)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Throws a JavaScript exception.
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
+ // Returns undefined, and schedules an exception to be thrown.
+ return v8::ThrowException(v8::Exception::Error(
+ v8::String::New("NumberFormat method called on an object "
+ "that is not a NumberFormat.")));
+}
+
+} } // namespace v8::internal
Property changes on: src/extensions/experimental/number-format.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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