Commit a60085b2 authored by cira@chromium.org's avatar cira@chromium.org

Re-landing http://codereview.chromium.org/7014019.

Adding DateTimeFormat class to i18n API with following methods:

- format
- getWeekdays
- getMonths
- get Eras
- getAmPm

Difference from the reverted revision:

Removed all references to v8/src, like ASSERT_EQ.

All #includes have full path to include/v8.h or extension headers.

TEST=Visit i18n.kaziprst.org/datetimeformat.html
Review URL: http://codereview.chromium.org/7105002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 22558609
This diff is collapsed.
// 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.
#ifndef V8_EXTENSIONS_EXPERIMENTAL_DATETIME_FORMAT_H_
#define V8_EXTENSIONS_EXPERIMENTAL_DATETIME_FORMAT_H_
#include "include/v8.h"
#include "unicode/uversion.h"
namespace U_ICU_NAMESPACE {
class SimpleDateFormat;
}
namespace v8 {
namespace internal {
class DateTimeFormat {
public:
static v8::Handle<v8::Value> JSDateTimeFormat(const v8::Arguments& args);
// Helper methods for various bindings.
// Unpacks date format object from corresponding JavaScript object.
static icu::SimpleDateFormat* UnpackDateTimeFormat(
v8::Handle<v8::Object> obj);
// Release memory we allocated for the DateFormat once the JS object that
// holds the pointer gets garbage collected.
static void DeleteDateTimeFormat(v8::Persistent<v8::Value> object,
void* param);
// Formats date and returns corresponding string.
static v8::Handle<v8::Value> Format(const v8::Arguments& args);
// All date time symbol methods below return stand-alone names in
// either narrow, abbreviated or wide width.
// Get list of months.
static v8::Handle<v8::Value> GetMonths(const v8::Arguments& args);
// Get list of weekdays.
static v8::Handle<v8::Value> GetWeekdays(const v8::Arguments& args);
// Get list of eras.
static v8::Handle<v8::Value> GetEras(const v8::Arguments& args);
// Get list of day periods.
static v8::Handle<v8::Value> GetAmPm(const v8::Arguments& args);
private:
DateTimeFormat();
static v8::Persistent<v8::FunctionTemplate> datetime_format_template_;
};
} } // namespace v8::internal
#endif // V8_EXTENSIONS_EXPERIMENTAL_DATETIME_FORMAT_H_
......@@ -41,6 +41,8 @@
'break-iterator.h',
'collator.cc',
'collator.h',
'datetime-format.cc',
'datetime-format.h',
'i18n-extension.cc',
'i18n-extension.h',
'i18n-locale.cc',
......
......@@ -29,6 +29,7 @@
#include "src/extensions/experimental/break-iterator.h"
#include "src/extensions/experimental/collator.h"
#include "src/extensions/experimental/datetime-format.h"
#include "src/extensions/experimental/i18n-locale.h"
#include "src/extensions/experimental/i18n-natives.h"
......@@ -49,6 +50,8 @@ v8::Handle<v8::FunctionTemplate> I18NExtension::GetNativeFunction(
return v8::FunctionTemplate::New(BreakIterator::JSBreakIterator);
} else if (name->Equals(v8::String::New("NativeJSCollator"))) {
return v8::FunctionTemplate::New(Collator::JSCollator);
} else if (name->Equals(v8::String::New("NativeJSDateTimeFormat"))) {
return v8::FunctionTemplate::New(DateTimeFormat::JSDateTimeFormat);
}
return v8::Handle<v8::FunctionTemplate>();
......
......@@ -29,6 +29,8 @@
#include <string.h>
#include "unicode/unistr.h"
namespace v8 {
namespace internal {
......@@ -40,4 +42,27 @@ void I18NUtils::StrNCopy(char* dest, int length, const char* src) {
dest[length - 1] = '\0';
}
// static
bool I18NUtils::ExtractStringSetting(const v8::Handle<v8::Object>& settings,
const char* setting,
icu::UnicodeString* result) {
if (!setting || !result) return false;
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
v8::Handle<v8::Value> value = settings->Get(v8::String::New(setting));
if (try_catch.HasCaught()) {
return false;
}
// No need to check if |value| is empty because it's taken care of
// by TryCatch above.
if (!value->IsUndefined() && !value->IsNull() && value->IsString()) {
v8::String::Utf8Value utf8_value(value);
if (*utf8_value == NULL) return false;
result->setTo(icu::UnicodeString::fromUTF8(*utf8_value));
return true;
}
return false;
}
} } // namespace v8::internal
......@@ -28,6 +28,14 @@
#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_
#define V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_
#include "include/v8.h"
#include "unicode/uversion.h"
namespace U_ICU_NAMESPACE {
class UnicodeString;
}
namespace v8 {
namespace internal {
......@@ -37,9 +45,14 @@ class I18NUtils {
// (length - 1) bytes.
// We can't use snprintf since it's not supported on all relevant platforms.
// We can't use OS::SNPrintF, it's only for internal code.
// TODO(cira): Find a way to use OS::SNPrintF instead.
static void StrNCopy(char* dest, int length, const char* src);
// Extract a string setting named in |settings| and set it to |result|.
// Return true if it's specified. Otherwise, return false.
static bool ExtractStringSetting(const v8::Handle<v8::Object>& settings,
const char* setting,
icu::UnicodeString* result);
private:
I18NUtils() {}
};
......
......@@ -45,11 +45,11 @@ v8Locale = function(settings) {
}
var properties = NativeJSLocale(
v8Locale.createSettingsOrDefault_(settings, {'localeID': 'root'}));
v8Locale.__createSettingsOrDefault(settings, {'localeID': 'root'}));
// Keep the resolved ICU locale ID around to avoid resolving localeID to
// ICU locale ID every time BreakIterator, Collator and so forth are called.
this.__icuLocaleID__ = properties.icuLocaleID;
this.__icuLocaleID = properties.icuLocaleID;
this.options = {'localeID': properties.localeID,
'regionID': properties.regionID};
};
......@@ -61,7 +61,7 @@ v8Locale = function(settings) {
*/
v8Locale.prototype.derive = function(settings) {
return new v8Locale(
v8Locale.createSettingsOrDefault_(settings, this.options));
v8Locale.__createSettingsOrDefault(settings, this.options));
};
/**
......@@ -74,14 +74,15 @@ v8Locale.prototype.derive = function(settings) {
* - word
* - sentence
* - line
* @private
* @constructor
*/
v8Locale.v8BreakIterator = function(locale, type) {
native function NativeJSBreakIterator();
locale = v8Locale.createLocaleOrDefault_(locale);
locale = v8Locale.__createLocaleOrDefault(locale);
// BCP47 ID would work in this case, but we use ICU locale for consistency.
var iterator = NativeJSBreakIterator(locale.__icuLocaleID__, type);
var iterator = NativeJSBreakIterator(locale.__icuLocaleID, type);
iterator.type = type;
return iterator;
};
......@@ -117,26 +118,110 @@ v8Locale.prototype.v8CreateBreakIterator = function(type) {
* - ignoreCase
* - ignoreAccents
* - numeric
* @private
* @constructor
*/
v8Locale.Collator = function(locale, settings) {
native function NativeJSCollator();
locale = v8Locale.createLocaleOrDefault_(locale);
locale = v8Locale.__createLocaleOrDefault(locale);
var collator = NativeJSCollator(
locale.__icuLocaleID__, v8Locale.createSettingsOrDefault_(settings, {}));
locale.__icuLocaleID, v8Locale.__createSettingsOrDefault(settings, {}));
return collator;
};
/**
* Creates new Collator based on current locale.
* @param {Object} - collation flags. See constructor.
* @returns {Object} - new v8BreakIterator object.
* @returns {Object} - new Collator object.
*/
v8Locale.prototype.createCollator = function(settings) {
return new v8Locale.Collator(this, settings);
};
/**
* DateTimeFormat class implements locale-aware date and time formatting.
* Constructor is not part of public API.
* @param {Object} locale - locale object to pass to formatter.
* @param {Object} settings - formatting flags:
* - skeleton
* - dateType
* - timeType
* - calendar
* @private
* @constructor
*/
v8Locale.__DateTimeFormat = function(locale, settings) {
native function NativeJSDateTimeFormat();
settings = v8Locale.__createSettingsOrDefault(settings, {});
var cleanSettings = {};
if (settings.hasOwnProperty('skeleton')) {
cleanSettings['skeleton'] = settings['skeleton'];
} else {
cleanSettings = {};
if (settings.hasOwnProperty('dateType')) {
var dt = settings['dateType'];
if (!/^short|medium|long|full$/.test(dt)) dt = 'short';
cleanSettings['dateType'] = dt;
}
if (settings.hasOwnProperty('timeType')) {
var tt = settings['timeType'];
if (!/^short|medium|long|full$/.test(tt)) tt = 'short';
cleanSettings['timeType'] = tt;
}
}
// Default is to show short date and time.
if (!cleanSettings.hasOwnProperty('skeleton') &&
!cleanSettings.hasOwnProperty('dateType') &&
!cleanSettings.hasOwnProperty('timeType')) {
cleanSettings = {'dateType': 'short',
'timeType': 'short'};
}
locale = v8Locale.__createLocaleOrDefault(locale);
var formatter = NativeJSDateTimeFormat(locale.__icuLocaleID, cleanSettings);
// NativeJSDateTimeFormat creates formatter.options for us, we just need
// to append actual settings to it.
for (key in cleanSettings) {
formatter.options[key] = cleanSettings[key];
}
/**
* Clones existing date time format with possible overrides for some
* of the options.
* @param {!Object} overrideSettings - overrides for current format settings.
* @returns {Object} - new DateTimeFormat object.
* @public
*/
formatter.derive = function(overrideSettings) {
// To remove a setting user can specify undefined as its value. We'll remove
// it from the map in that case.
for (var prop in overrideSettings) {
if (settings.hasOwnProperty(prop) && !overrideSettings[prop]) {
delete settings[prop];
}
}
return new v8Locale.__DateTimeFormat(
locale, v8Locale.__createSettingsOrDefault(overrideSettings, settings));
};
return formatter;
};
/**
* Creates new DateTimeFormat based on current locale.
* @param {Object} - formatting flags. See constructor.
* @returns {Object} - new DateTimeFormat object.
*/
v8Locale.prototype.createDateTimeFormat = function(settings) {
return new v8Locale.__DateTimeFormat(this, settings);
};
/**
* Merges user settings and defaults.
* Settings that are not of object type are rejected.
......@@ -145,8 +230,9 @@ v8Locale.prototype.createCollator = function(settings) {
* @param {!Object} settings - user provided settings.
* @param {!Object} defaults - default values for this type of settings.
* @returns {Object} - valid settings object.
* @private
*/
v8Locale.createSettingsOrDefault_ = function(settings, defaults) {
v8Locale.__createSettingsOrDefault = function(settings, defaults) {
if (!settings || typeof(settings) !== 'object' ) {
return defaults;
}
......@@ -155,11 +241,17 @@ v8Locale.createSettingsOrDefault_ = function(settings, defaults) {
settings[key] = defaults[key];
}
}
// Clean up values, like trimming whitespace.
// Clean up settings.
for (var key in settings) {
// Trim whitespace.
if (typeof(settings[key]) === "string") {
settings[key] = settings[key].trim();
}
// Remove all properties that are set to undefined/null. This allows
// derive method to remove a setting we don't need anymore.
if (!settings[key]) {
delete settings[key];
}
}
return settings;
......@@ -170,8 +262,9 @@ v8Locale.createSettingsOrDefault_ = function(settings, defaults) {
* we create default locale and return it.
* @param {!Object} locale - user provided locale.
* @returns {Object} - v8Locale object.
* @private
*/
v8Locale.createLocaleOrDefault_ = function(locale) {
v8Locale.__createLocaleOrDefault = function(locale) {
if (!locale || !(locale instanceof v8Locale)) {
return new v8Locale();
} else {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment