From 6a6bdf293b772dda5581d75b3b934033a05fa237 Mon Sep 17 00:00:00 2001 From: Piotr Kosko Date: Tue, 27 Oct 2015 11:05:17 +0100 Subject: [PATCH] [Cordova][Globalization] Added C++ layer for missing functionalities. [Verification] All globalization tests pass Change-Id: Iaeade03161404f6e8510dd100b792c136d96bfa4 Signed-off-by: Piotr Kosko --- src/globalization/cordova_globalization.gyp | 6 + src/globalization/cordova_globalization_api.js | 312 +++++++++----- .../cordova_globalization_extension.cc | 18 +- .../cordova_globalization_extension.h | 10 +- .../cordova_globalization_instance.cc | 467 +++++++++++++++++++++ src/globalization/cordova_globalization_instance.h | 47 +++ src/globalization/cordova_globalization_tools.cc | 394 +++++++++++++++++ src/globalization/cordova_globalization_tools.h | 87 ++++ 8 files changed, 1219 insertions(+), 122 deletions(-) create mode 100644 src/globalization/cordova_globalization_instance.cc create mode 100644 src/globalization/cordova_globalization_instance.h create mode 100644 src/globalization/cordova_globalization_tools.cc create mode 100644 src/globalization/cordova_globalization_tools.h diff --git a/src/globalization/cordova_globalization.gyp b/src/globalization/cordova_globalization.gyp index 9c44927..82599f6 100644 --- a/src/globalization/cordova_globalization.gyp +++ b/src/globalization/cordova_globalization.gyp @@ -10,6 +10,10 @@ 'cordova_globalization_api.js', 'cordova_globalization_extension.cc', 'cordova_globalization_extension.h', + 'cordova_globalization_instance.cc', + 'cordova_globalization_instance.h', + 'cordova_globalization_tools.cc', + 'cordova_globalization_tools.h', ], 'include_dirs': [ '../', @@ -18,6 +22,8 @@ 'variables': { 'packages': [ 'webapi-plugins', + 'icu-i18n', + 'vconf' ], }, }, diff --git a/src/globalization/cordova_globalization_api.js b/src/globalization/cordova_globalization_api.js index e857ba9..d15fee5 100755 --- a/src/globalization/cordova_globalization_api.js +++ b/src/globalization/cordova_globalization_api.js @@ -14,6 +14,9 @@ * limitations under the License. */ +var utils_ = xwalk.utils; +var native_ = new utils_.NativeManager(extension); + var _navigator = navigator || {}; var _global = window || global || {}; @@ -47,6 +50,15 @@ var formatMediumStr = 'medium'; var formatLongStr = 'long'; var formatFullStr = 'full'; +var typeWide = 'wide'; +var typeNarrow = 'narrow'; +var itemMonths = 'months'; +var itemDays = 'days'; + +var numberTypeDecimal = 'decimal'; +var numberTypePercent = 'percent'; +var numberTypeCurrency = 'currency'; + var oneHourSeconds = 60*60; var Globalization = {}; @@ -64,7 +76,7 @@ Globalization.getPreferredLanguage = function(successCb, errorCb) { function(error) { console.log('Cordova, getLocaleName, An error occurred ' + error.message); errorCb(new GlobalizationError(GlobalizationError.UNKNOWN_ERROR , - 'cannot retrieve language name')); + 'cannot retrieve language name')); } ); } @@ -73,125 +85,143 @@ Globalization.getLocaleName = function(successCb, errorCb) { Globalization.getPreferredLanguage(successCb, errorCb); } -//TODO dateToString would support only full length (one format is supprted), +//TODO JS implementation of dateToString would support only full length (one format is supprted), // but selector for getting only needed values is fully supported Globalization.dateToString = function(date, successCb, errorCb, options) { // TODO add validation of parameters var result = null; var formatLength = formatFullStr; var selector = selectorDateAndTimeStr; - console.log("options " + JSON.stringify(options)); if (options) { formatLength = options.formatLength || formatFullStr; selector = options.selector || selectorDateAndTimeStr; } - console.log("len: " + formatLength + " selector: " + selector); - var tzdate = new tizen.TZDate(date); - if (tzdate) { - // TODO only one format length is supprted - // "Wednesday, January 7, 2015, 12:33:15 PM" - if (selectorDateStr === selector) { - result = tzdate.toLocaleDateString(); - } else if (selectorTimeStr === selector) { - result = tzdate.toLocaleTimeString(); + var timestamp = date.getTime(); + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.FORMATTING_ERROR , native_.getErrorObject(result).message); + native_.callIfPossible(errorCb, error); } else { - result = tzdate.toLocaleString(); + successCb(native_.getResultObject(result)); } - } - - if (result) { - setTimeout( function() { - successCb ({'value': result}); - }, 0); - } else { - setTimeout( function() { - errorCb(new GlobalizationError( - GlobalizationError.FORMATTING_ERROR , 'cannot format date string')); - }, 0); - } + }; + var callArgs = { + formatLength: String(formatLength), + selector: String(selector), + timestamp: String(timestamp) + }; + native_.call('CordovaGlobalization_dateToString', callArgs, callback); } //TODO implementation would try to convert string to Date using javascript Date object // constructor, options are basically ignored Globalization.stringToDate = function(dateString, successCb, errorCb, options) { // TODO add validation of parameters - var d = new Date(dateString); - if (!d.getTime()) { - setTimeout( function() { - errorCb(new GlobalizationError( - GlobalizationError.PARSING_ERROR , 'cannot parse date from string')); - }, 0); - } else { - var result = { - year : d.getYear() + 1900, - month : d.getMonth(), - day : d.getDate(), - hour : d.getHours(), - minute : d.getMinutes(), - second : d.getSeconds(), - millisecond : d.getMilliseconds() - }; - setTimeout( function() { - successCb (result); - }, 0); + var result = null; + var formatLength = formatFullStr; + var selector = selectorDateAndTimeStr; + if (options) { + formatLength = options.formatLength || formatFullStr; + selector = options.selector || selectorDateAndTimeStr; } + + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.PARSING_ERROR , native_.getErrorObject(result).message); + native_.callIfPossible(errorCb, error); + } else { + successCb(native_.getResultObject(result)); + } + }; + var callArgs = { + formatLength: String(formatLength), + selector: String(selector), + dateString : String(dateString) + }; + native_.call('CordovaGlobalization_stringToDate', callArgs, callback); } -// TODO getDatePattern would support only short and full length, -// but selector for getting only needed values is fully supported Globalization.getDatePattern = function(successCb, errorCb, options) { // TODO add validation of parameters + var formatLength = formatFullStr; var selector = selectorDateAndTimeStr; - var isShortFormat = false; + if (options) { + formatLength = options.formatLength || formatFullStr; selector = options.selector || selectorDateAndTimeStr; - isShortFormat = (options.formatLength === formatShortStr); - } - var pattern = null; - if (selectorTimeStr === selector) { - pattern = tizen.time.getTimeFormat(); - } else if (selectorDateStr === selector) { - pattern = tizen.time.getDateFormat(isShortFormat); - } else { - // TODO in tizen there is no unified date and time format getter - // (for now implementation separates date and time formats with colon ',' - pattern = tizen.time.getDateFormat(isShortFormat) + ", " + tizen.time.getTimeFormat(); } - var currentDateTime = tizen.time.getCurrentDateTime(); - if (pattern && currentDateTime) { - // TODO currently value as "GMT+09:00" will be returned, - // to get value "Asia/Seoul" use .getTimezone() instead - var timezoneAbbreviation = currentDateTime.getTimezoneAbbreviation(); - - // TODO method secondsFromUTC returns inverted offset: if time zone is GMT+8, it will return -32,400. - // TODO currently utcOffset will include DST additional hour if it is present, value will be - // timezoneOffset = timezoneOffsetWithoutDST + DSTAdditionalOffset - // if other behaviour is correct, just need to substract dstOffset from utcOffset - var utcOffset = currentDateTime.secondsFromUTC() * (-1); - var dstOffset = currentDateTime.isDST() ? oneHourSeconds : 0; - - var result = { - "pattern": pattern, - "timezone": timezoneAbbreviation, - "utc_offset": utcOffset, - "dst_offset": dstOffset - }; - setTimeout( function() { - successCb (result); - }, 0); - } else { - errorCb(new GlobalizationError(GlobalizationError.PATTERN_ERROR , "cannot get pattern")); - } + var callback = function(result) { + // Checking succes of gathering pattern + var fullResult = {}; + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.PATTERN_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + return; + } else { + // not calling success callback yet + fullResult = native_.getResultObject(result); + } + + // looking for missing pieces of fullResult object + var currentDateTime = tizen.time.getCurrentDateTime(); + if (currentDateTime) { + // TODO currently value as "GMT+09:00" will be returned, + // to get value "Asia/Seoul" use .getTimezone() instead + var timezoneAbbreviation = currentDateTime.getTimezoneAbbreviation(); + + // TODO method secondsFromUTC returns inverted offset: if time zone is GMT+8, it will return -32,400. + // TODO currently utcOffset will include DST additional hour if it is present, value will be + // timezoneOffset = timezoneOffsetWithoutDST + DSTAdditionalOffset + // if other behaviour is correct, just need to substract dstOffset from utcOffset + var utcOffset = currentDateTime.secondsFromUTC() * (-1); + var dstOffset = currentDateTime.isDST() ? oneHourSeconds : 0; + + //adding missing parts of result + fullResult["timezone"] = timezoneAbbreviation; + fullResult["utc_offset"] = utcOffset; + fullResult["dst_offset"] = dstOffset; + successCb(fullResult); + } else { + var error = new GlobalizationError( + GlobalizationError.PATTERN_ERROR , "cannot get pattern"); + native_.callIfPossible(errorCb, error); + } + }; + var callArgs = { + formatLength: String(formatLength), + selector: String(selector) + }; + native_.call('CordovaGlobalization_getDatePattern', callArgs, callback); } -// TODO implement this as native method Globalization.getDateNames = function(successCb, errorCb, options) { // TODO add validation of parameters - setTimeout( function() { - errorCb(new GlobalizationError(GlobalizationError.UNKNOWN_ERROR , "unsupported")) - }, 0); + var type = typeWide; + var item = itemDays; + if (options) { + type = options.type || typeWide; + item = options.item || itemDays; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.UNKNOWN_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + } else { + successCb(native_.getResultObject(result)); + } + }; + var callArgs = { + type: String(type), + item: String(item) + }; + native_.call('CordovaGlobalization_getDateNames', callArgs, callback); } Globalization.isDayLightSavingsTime = function(date, successCb, errorCb) { @@ -209,53 +239,105 @@ Globalization.isDayLightSavingsTime = function(date, successCb, errorCb) { } } -//TODO implement this as native method Globalization.getFirstDayOfWeek = function(successCb, errorCb) { // TODO add validation of parameters - setTimeout( function() { - errorCb(new GlobalizationError(GlobalizationError.UNKNOWN_ERROR , "unsupported")) - }, 0); + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.UNKNOWN_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + } else { + successCb(native_.getResultObject(result)); + } + }; + native_.call('CordovaGlobalization_getFirstDayOfWeek', {}, callback); } -//TODO how to implement this?? Globalization.numberToString = function(number, successCb, errorCb, options) { // TODO add validation of parameters - var result = number.toLocaleString(); - setTimeout( function() { - successCb ( {'value' : result} ); - }, 0); + var type = numberTypeDecimal; + if (options) { + type = options.type || numberTypeDecimal; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.FORMATTING_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + } else { + successCb(native_.getResultObject(result)); + } + }; + var callArgs = { + number: String(number), + type: String(type) + }; + native_.call('CordovaGlobalization_numberToString', callArgs, callback); } -//TODO how should look this implementation about options?? Globalization.stringToNumber = function(numberStr, successCb, errorCb, options) { // TODO add validation of parameters - var result = Number(numberStr); - if ('NaN' != result.toString()) { - setTimeout( function() { - successCb ( {'value' : result} ); - }, 0); - } else { - setTimeout( function() { - errorCb(new GlobalizationError(GlobalizationError.PARSING_ERROR , - "cannot convert string to number")) - }, 0); + var type = numberTypeDecimal; + if (options) { + type = options.type || numberTypeDecimal; } + + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.PARSING_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + } else { + var result = native_.getResultObject(result); + result.value = Number(result.value); + successCb(result); + } + }; + var callArgs = { + number: String(numberStr), + type: String(type) + }; + native_.call('CordovaGlobalization_stringToNumber', callArgs, callback); } -//TODO how to implement this?? Globalization.getNumberPattern = function(successCb, errorCb, options) { // TODO add validation of parameters - setTimeout( function() { - errorCb(new GlobalizationError(GlobalizationError.UNKNOWN_ERROR , "unsupported")) - }, 0); + var type = numberTypeDecimal; + if (options) { + type = options.type || numberTypeDecimal; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.UNKNOWN_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + } else { + successCb(native_.getResultObject(result)); + } + }; + var callArgs = { + type: String(type) + }; + native_.call('CordovaGlobalization_getNumberPattern', callArgs, callback); } -//TODO how to implement this?? Globalization.getCurrencyPattern = function(currencyCode, successCb, errorCb) { // TODO add validation of parameters - setTimeout( function() { - errorCb(new GlobalizationError(GlobalizationError.UNKNOWN_ERROR , "unsupported")) - }, 0); + var callback = function(result) { + if (native_.isFailure(result)) { + var error = new GlobalizationError( + GlobalizationError.UNKNOWN_ERROR , native_.getErrorObject(result).message) + native_.callIfPossible(errorCb, error); + } else { + successCb(native_.getResultObject(result)); + } + }; + var callArgs = { + currencyCode : String(currencyCode) + }; + native_.call('CordovaGlobalization_getCurrencyPattern', callArgs, callback); } _navigator.globalization = Globalization; diff --git a/src/globalization/cordova_globalization_extension.cc b/src/globalization/cordova_globalization_extension.cc index 7bb780d..ddf1d3d 100755 --- a/src/globalization/cordova_globalization_extension.cc +++ b/src/globalization/cordova_globalization_extension.cc @@ -15,11 +15,13 @@ */ #include "globalization/cordova_globalization_extension.h" +#include "globalization/cordova_globalization_instance.h" // This will be generated from cordova_globalization_api.js extern const char kSource_cordova_globalization_api[]; common::Extension* CreateExtension() { + LoggerD("Entered"); return new extension::cordova::globalization::CordovaGlobalizationExtension(); } @@ -28,12 +30,20 @@ namespace cordova { namespace globalization { CordovaGlobalizationExtension::CordovaGlobalizationExtension() { + LoggerD("Entered"); SetExtensionName("tizen.cordova.globalization"); SetJavaScriptAPI(kSource_cordova_globalization_api); } -CordovaGlobalizationExtension::~CordovaGlobalizationExtension() {} +CordovaGlobalizationExtension::~CordovaGlobalizationExtension() { + LoggerD("Entered"); +} + +common::Instance* CordovaGlobalizationExtension::CreateInstance() { + LoggerD("Entered"); + return new extension::cordova::globalization::CordovaGlobalizationInstance(); +} -} // globalization -} // cordova -} // extension +} // globalization +} // cordova +} // extension diff --git a/src/globalization/cordova_globalization_extension.h b/src/globalization/cordova_globalization_extension.h index 91a37ec..cf053aa 100755 --- a/src/globalization/cordova_globalization_extension.h +++ b/src/globalization/cordova_globalization_extension.h @@ -27,10 +27,14 @@ class CordovaGlobalizationExtension : public common::Extension { public: CordovaGlobalizationExtension(); virtual ~CordovaGlobalizationExtension(); + + private: + // common::Extension implementation. + virtual common::Instance* CreateInstance(); }; -} // globalization -} // cordova -} // extension +} // globalization +} // cordova +} // extension #endif // GLOBALIZATION_CORDOVA_GLOBALIZATION_EXTENSION_H_ diff --git a/src/globalization/cordova_globalization_instance.cc b/src/globalization/cordova_globalization_instance.cc new file mode 100644 index 0000000..721039a --- /dev/null +++ b/src/globalization/cordova_globalization_instance.cc @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "globalization/cordova_globalization_instance.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "globalization/cordova_globalization_tools.h" + +namespace extension { +namespace cordova { +namespace globalization { + +using std::string; +using common::ErrorCode; +using common::PlatformResult; +using common::Instance; +using common::TaskQueue; + +CordovaGlobalizationInstance::CordovaGlobalizationInstance() { + using std::placeholders::_1; + using std::placeholders::_2; + + LoggerD("Entered"); + +#define REGISTER_SYNC(c, x) \ + RegisterSyncHandler(c, std::bind(&CordovaGlobalizationInstance::x, this, _1, _2)); +#define REGISTER_ASYNC(c, x) \ + RegisterSyncHandler(c, std::bind(&CordovaGlobalizationInstance::x, this, _1, _2)); + + REGISTER_SYNC("CordovaGlobalization_dateToString", DateToString); + REGISTER_SYNC("CordovaGlobalization_stringToDate", StringToDate); + REGISTER_SYNC("CordovaGlobalization_getDatePattern", GetDatePattern); + REGISTER_SYNC("CordovaGlobalization_getDateNames", GetDateNames); + REGISTER_SYNC("CordovaGlobalization_getFirstDayOfWeek", GetFirstDayOfWeek); + REGISTER_SYNC("CordovaGlobalization_numberToString", NumberToString); + REGISTER_SYNC("CordovaGlobalization_stringToNumber", StringToNumber); + REGISTER_SYNC("CordovaGlobalization_getNumberPattern", GetNumberPattern); + REGISTER_SYNC("CordovaGlobalization_getCurrencyPattern", GetCurrencyPattern); + + +#undef REGISTER_SYNC +#undef REGISTER_ASYNC +} + +CordovaGlobalizationInstance::~CordovaGlobalizationInstance() { + LoggerD("Entered"); +} + +void CordovaGlobalizationInstance::DateToString(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("formatLength") || !args.contains("selector") || !args.contains("timestamp") || + !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + + const std::string& format_length = args.get("formatLength").get(); + const std::string& selector = args.get("selector").get(); + const std::string& timestamp_str = args.get("timestamp").get(); + UDate date = std::stod(timestamp_str); + + auto get = [this, format_length, selector, date](const std::shared_ptr& response) -> void { + string result_str = CordovaGlobalizationTools::GetDateString( + date, CordovaGlobalizationTools::GetDateFormat(format_length), selector); + + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + result_obj.insert(std::make_pair("value", picojson::value(result_str))); + ReportSuccess(result, response->get()); + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::StringToDate(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("formatLength") || !args.contains("selector") || !args.contains("dateString") || + !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + + const std::string& format_length = args.get("formatLength").get(); + const std::string& selector = args.get("selector").get(); + const std::string& date_str = args.get("dateString").get(); + + auto get = [this, format_length, selector, date_str](const std::shared_ptr& response) -> void { + UDate result_date = 0; + PlatformResult ret = CordovaGlobalizationTools::GetUDateFromString( + date_str, CordovaGlobalizationTools::GetDateFormat(format_length), selector, &result_date); + + if (ret.IsSuccess()) { + // UDate holds milliseconds, conversion to time_t needs seconds + time_t seconds_ts = (time_t)(result_date / 1000); + + struct tm * result_time = localtime(&seconds_ts); + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + result_obj.insert(std::make_pair( + "year", picojson::value(static_cast(result_time->tm_year + 1900)))); + result_obj.insert(std::make_pair( + "month", picojson::value(static_cast(result_time->tm_mon)))); + result_obj.insert(std::make_pair( + "day", picojson::value(static_cast(result_time->tm_mday)))); + result_obj.insert(std::make_pair( + "hour", picojson::value(static_cast(result_time->tm_hour)))); + result_obj.insert(std::make_pair( + "minute", picojson::value(static_cast(result_time->tm_min)))); + result_obj.insert(std::make_pair( + "second", picojson::value(static_cast(result_time->tm_sec)))); + result_obj.insert(std::make_pair( + "millisecond", picojson::value(static_cast(0.0)))); + + ReportSuccess(result, response->get()); + } else { + ReportError(ret, &(response->get())); + } + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::GetDatePattern(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("formatLength") || !args.contains("selector") || !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + + const std::string& format_length = args.get("formatLength").get(); + const std::string& selector = args.get("selector").get(); + + auto get = [this, format_length, selector](const std::shared_ptr& response) -> void { + string result_str; + PlatformResult ret = CordovaGlobalizationTools::GetDatePattern( + CordovaGlobalizationTools::GetDateFormat(format_length), selector, &result_str); + + if (ret.IsSuccess()) { + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + + // returning only pattern of date, rest of result should be added in JS using web device API + result_obj.insert(std::make_pair("pattern", picojson::value(result_str))); + + ReportSuccess(result, response->get()); + } else { + ReportError(ret, &(response->get())); + } + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::GetDateNames(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("type") || !args.contains("item") || !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + + const std::string& type = args.get("type").get(); + const std::string& item = args.get("item").get(); + + auto get = [this, type, item](const std::shared_ptr& response) -> void { + std::vector items; + PlatformResult ret = CordovaGlobalizationTools::GetNames(item, type, &items); + + if (ret.IsError() || items.empty()) { + LoggerE("Cannot get names for %s", item.c_str()); + ReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get names."), + &(response->get())); + } + + // creating json array + picojson::value result_array = picojson::value(picojson::array()); + picojson::array& array_obj = result_array.get(); + for (size_t i = 0 ; i < items.size(); i++) { + array_obj.push_back(picojson::value(items[i])); + } + + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + result_obj.insert(std::make_pair("value", result_array)); + ReportSuccess(result, response->get()); + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::GetFirstDayOfWeek(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + + auto get = [this](const std::shared_ptr& response) -> void { + double first_day_value = 0; + PlatformResult ret = CordovaGlobalizationTools::GetFirstDayOfWeek(&first_day_value); + if (ret.IsSuccess()) { + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + result_obj.insert(std::make_pair("value", picojson::value(first_day_value))); + ReportSuccess(result, response->get()); + } else { + ReportError(ret, &(response->get())); + } + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::NumberToString(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("type") || !args.contains("number") || !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + + // this type of conversion of double is locale independent, always parse dots (JS send number with dot) + double number = 0.0f; + std::istringstream istr(args.get("number").get()); + istr >> number; + + const std::string& type = args.get("type").get(); + + auto get = [this, type, number](const std::shared_ptr& response) -> void { + std::string result_str; + PlatformResult ret = CordovaGlobalizationTools::FormatNumber(number, type, &result_str); + if (ret.IsSuccess()) { + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + result_obj.insert(std::make_pair("value", picojson::value(result_str))); + ReportSuccess(result, response->get()); + } else { + ReportError(ret, &(response->get())); + } + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::StringToNumber(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("type") || !args.contains("number") || !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + const std::string& number_str = args.get("number").get(); + const std::string& type = args.get("type").get(); + + auto get = [this, type, number_str](const std::shared_ptr& response) -> void { + double result_num = 0; + PlatformResult ret = CordovaGlobalizationTools::ParseNumber(number_str, type, &result_num); + + if (ret.IsSuccess()) { + // replacing ',' for '.' if any exist, locale independence (JS need number with dot) + std::string result_str = std::to_string(result_num); + std::replace(result_str.begin(), result_str.end(), ',', '.'); + + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + result_obj.insert(std::make_pair("value", picojson::value(result_str))); + ReportSuccess(result, response->get()); + } else { + ReportError(ret, &(response->get())); + } + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::GetNumberPattern(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("type") || !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + const std::string& type = args.get("type").get(); + + auto get = [this, type](const std::shared_ptr& response) -> void { + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + + std::string res_pattern, res_symbol, res_positive, res_negative, res_decimal, res_grouping; + double res_fraction = 0; + double res_rounding = 0; + + CordovaGlobalizationTools::GetNumberPattern(type, &res_pattern, &res_symbol, &res_fraction, + &res_rounding, &res_positive, &res_negative, + &res_decimal, &res_grouping); + result_obj.insert(std::make_pair("pattern", picojson::value(res_pattern))); + result_obj.insert(std::make_pair("symbol", picojson::value(res_symbol))); + result_obj.insert(std::make_pair("fraction", picojson::value(res_fraction))); + result_obj.insert(std::make_pair("rounding", picojson::value(res_rounding))); + result_obj.insert(std::make_pair("positive", picojson::value(res_positive))); + result_obj.insert(std::make_pair("negative", picojson::value(res_negative))); + result_obj.insert(std::make_pair("decimal", picojson::value(res_decimal))); + result_obj.insert(std::make_pair("grouping", picojson::value(res_grouping))); + + ReportSuccess(result, response->get()); + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +void CordovaGlobalizationInstance::GetCurrencyPattern(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + if (!args.contains("currencyCode") || !args.contains("callbackId")) { + LoggerE("Invalid parameter passed."); + ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), &out); + return; + } + const double callback_id = args.get("callbackId").get(); + const std::string& code = args.get("currencyCode").get(); + + auto get = [this, code](const std::shared_ptr& response) -> void { + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + + std::string res_pattern, res_decimal, res_grouping; + double res_fraction = 0; + double res_rounding = 0; + + CordovaGlobalizationTools::GetCurrencyPattern(code, &res_pattern, &res_fraction, + &res_rounding, &res_decimal, &res_grouping); + result_obj.insert(std::make_pair("pattern", picojson::value(res_pattern))); + result_obj.insert(std::make_pair("code", picojson::value(code))); + result_obj.insert(std::make_pair("fraction", picojson::value(res_fraction))); + result_obj.insert(std::make_pair("rounding", picojson::value(res_rounding))); + result_obj.insert(std::make_pair("decimal", picojson::value(res_decimal))); + result_obj.insert(std::make_pair("grouping", picojson::value(res_grouping))); + + ReportSuccess(result, response->get()); + }; + + auto get_response = [this, callback_id](const std::shared_ptr& response) -> void { + LoggerD("Getting response"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair("callbackId", picojson::value{static_cast(callback_id)})); + LoggerD("message: %s", response->serialize().c_str()); + Instance::PostMessage(this, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + TaskQueue::GetInstance().Queue(get, get_response, data); +} + +} // globalization +} // cordova +} // extension diff --git a/src/globalization/cordova_globalization_instance.h b/src/globalization/cordova_globalization_instance.h new file mode 100644 index 0000000..d4fc780 --- /dev/null +++ b/src/globalization/cordova_globalization_instance.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLOBALIZATION_CORDOVA_GLOBALIZATION_INSTANCE_H_ +#define GLOBALIZATION_CORDOVA_GLOBALIZATION_INSTANCE_H_ + +#include +#include + +namespace extension { +namespace cordova { +namespace globalization { + +class CordovaGlobalizationInstance : public common::ParsedInstance { + public: + CordovaGlobalizationInstance(); + virtual ~CordovaGlobalizationInstance(); + + private: + void DateToString(const picojson::value& args, picojson::object& out); + void StringToDate(const picojson::value& args, picojson::object& out); + void GetDatePattern(const picojson::value& args, picojson::object& out); + void GetDateNames(const picojson::value& args, picojson::object& out); + void GetFirstDayOfWeek(const picojson::value& args, picojson::object& out); + void NumberToString(const picojson::value& args, picojson::object& out); + void StringToNumber(const picojson::value& args, picojson::object& out); + void GetNumberPattern(const picojson::value& args, picojson::object& out); + void GetCurrencyPattern(const picojson::value& args, picojson::object& out); +}; +} // globalization +} // cordova +} // extension + +#endif // GLOBALIZATION_CORDOVA_GLOBALIZATION_INSTANCE_H_ diff --git a/src/globalization/cordova_globalization_tools.cc b/src/globalization/cordova_globalization_tools.cc new file mode 100644 index 0000000..a42680c --- /dev/null +++ b/src/globalization/cordova_globalization_tools.cc @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "globalization/cordova_globalization_tools.h" +#include +#include +#include +#include +#include + +namespace extension { +namespace cordova { +namespace globalization { + +using std::string; +using common::ErrorCode; +using common::PlatformResult; + +const std::string kSelectorDateStr = "date"; +const std::string kSelectorTimeStr = "time"; + +const std::string kFormatShortStr = "short"; +const std::string kFormatMediumStr = "medium"; +const std::string kFormatLongStr = "long"; +const std::string kFormatFullStr = "full"; + +const std::string kItemMonths = "months"; +const std::string kItemDays = "days"; + +const std::string kTypeWide = "wide"; +const std::string kTypeNarrow = "narrow"; + +const std::string kNumberTypeDecimal = "decimal"; +const std::string kNumberTypePercent = "percent"; +const std::string kNumberTypeCurrency = "currency"; + +Locale CordovaGlobalizationTools::GetDefaultLocale() { + //TODO add gathering locale according to settings + return Locale::createFromName("en_US"); + //return Locale::getUK(); +} + +std::string CordovaGlobalizationTools::ToUTF8String(const UnicodeString& uni_str) { + LoggerD("Entered"); + int buffer_len = sizeof(UChar) * uni_str.length() + 1; + std::unique_ptr result_buffer(static_cast(malloc(buffer_len)), + &std::free); + if (!result_buffer) { + return ""; + } + + memset(result_buffer.get(), 0, buffer_len); + CheckedArrayByteSink sink(result_buffer.get(), buffer_len); + uni_str.toUTF8(sink); + + if (sink.Overflowed()) { + return ""; + } + + return result_buffer.get(); +} + +DateFormat::EStyle CordovaGlobalizationTools::GetDateFormat(const std::string& length) { + LoggerD("Entered"); + if (kFormatShortStr == length) { + return DateFormat::kShort; + } else if (kFormatMediumStr == length) { + return DateFormat::kMedium; + } else if (kFormatLongStr == length) { + return DateFormat::kLong; + } + // default length would be full + return DateFormat::kFull; +} + +std::unique_ptr CordovaGlobalizationTools::GetDateFormatPtr(DateFormat::EStyle format, + const std::string& selector) { + LoggerD("Entered"); + bool foundDate = (std::string::npos != selector.find(kSelectorDateStr)); + bool foundTime = (std::string::npos != selector.find(kSelectorTimeStr)); + + std::unique_ptr dfmt; + Locale l = GetDefaultLocale(); + if (foundDate && !foundTime) { + dfmt.reset(DateFormat::createDateInstance(format, l)); + } else if (!foundDate && foundTime) { + dfmt.reset(DateFormat::createTimeInstance(format, l)); + } else { + dfmt.reset(DateFormat::createDateTimeInstance(format, format, l)); + } + return dfmt; +} + +std::string CordovaGlobalizationTools::GetDateString(UDate date, DateFormat::EStyle format, + const std::string& selector) { + LoggerD("Entered"); + UnicodeString str; + std::unique_ptr dfmt = GetDateFormatPtr(format, selector); + + dfmt->format(date, str); + return ToUTF8String(str); +} + +PlatformResult CordovaGlobalizationTools::GetUDateFromString(const std::string& date, + DateFormat::EStyle format, + const std::string& selector, + UDate* result) { + LoggerD("Entered"); + UErrorCode ec = U_ZERO_ERROR; + std::unique_ptr dfmt = GetDateFormatPtr(format, selector); + + UDate tmp_result = dfmt->parse(UnicodeString(date.c_str()), ec); + if (U_ZERO_ERROR >= ec) { + *result = tmp_result; + return PlatformResult(ErrorCode::NO_ERROR); + } else { + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not parse date"); + } +} + +PlatformResult CordovaGlobalizationTools::GetDatePattern(DateFormat::EStyle format, + const std::string& selector, std::string* result) { + LoggerD("Entered"); + UErrorCode ec = U_ZERO_ERROR; + UnicodeString res; + std::unique_ptr dfmt = GetDateFormatPtr(format, selector); + + if (SimpleDateFormat::getStaticClassID() != dfmt->getDynamicClassID()) { + LoggerE("Could not cast to SimpleDateFormat, operation failed"); + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not cast to SimpleDateFormat"); + } else { + LoggerE("Casting to SimpleDateFormat is allowed"); + } + + SimpleDateFormat* sdf = dynamic_cast(dfmt.get()); + if (sdf) { + std::string tmp_result = ToUTF8String(sdf->toLocalizedPattern(res, ec)); + if (U_ZERO_ERROR >= ec) { + *result = tmp_result; + return PlatformResult(ErrorCode::NO_ERROR); + } + } + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not get date pattern"); +} + +PlatformResult CordovaGlobalizationTools::GetNames(const std::string& item, + const std::string& type, + std::vector* result) { + LoggerD("Entered"); + std::vector tmp_result; + int32_t count = 0; + UErrorCode ec = U_ZERO_ERROR; + icu::DateFormatSymbols dfs = DateFormatSymbols(GetDefaultLocale(), ec); + if (U_ZERO_ERROR >= ec) { + UnicodeString* names_vector = nullptr; // DateFormatSymbols retains ownership. + if (kItemMonths == item) { + if (kTypeWide == type) { + names_vector = const_cast(dfs.getMonths(count)); + } else { + names_vector = const_cast(dfs.getShortMonths(count)); + } + } else { + if (kTypeWide == type) { + names_vector = const_cast(dfs.getWeekdays(count)); + } else { + names_vector = const_cast(dfs.getShortWeekdays(count)); + } + } + + if (names_vector) { + for (int i = 0; i < count;++i) { + if (names_vector[i].length() > 0) { + tmp_result.push_back(ToUTF8String(names_vector[i])); + } + } + } + *result = tmp_result; + return PlatformResult(ErrorCode::NO_ERROR); + } else { + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not get days names"); + } +} + +PlatformResult CordovaGlobalizationTools::GetFirstDayOfWeek(double* result) { + LoggerD("Entered"); + UnicodeString str; + UErrorCode ec = U_ZERO_ERROR; + std::unique_ptr dfmt(DateFormat::createDateInstance(DateFormat::kFull, + GetDefaultLocale())); + + UCalendarDaysOfWeek first_day = dfmt->getCalendar()->getFirstDayOfWeek(ec); + if (U_ZERO_ERROR >= ec) { + *result = static_cast(first_day); + return PlatformResult(ErrorCode::NO_ERROR); + } else { + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not get day first of week"); + } +} + +PlatformResult CordovaGlobalizationTools::FormatNumber(double number, const std::string& type, + std::string* result) { + LoggerD("Entered"); + UErrorCode ec = U_ZERO_ERROR; + UnicodeString str; + Locale l = GetDefaultLocale(); + + std::unique_ptr nfmt; + if (kNumberTypeCurrency == type) { + nfmt.reset(NumberFormat::createCurrencyInstance(l, ec)); + } else if (kNumberTypePercent == type) { + nfmt.reset(NumberFormat::createPercentInstance(l, ec)); + } else { + nfmt.reset(NumberFormat::createInstance(l, ec)); + } + if (U_ZERO_ERROR >= ec) { + nfmt->format(number, str); + *result = ToUTF8String(str);; + return PlatformResult(ErrorCode::NO_ERROR); + } else { + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not format number"); + } +} + +PlatformResult CordovaGlobalizationTools::ParseNumber(const std::string& number_str, + const std::string& type, + double* result) { + LoggerD("Entered"); + UErrorCode ec = U_ZERO_ERROR; + Locale l = GetDefaultLocale(); + UnicodeString str(number_str.c_str()); + PlatformResult res(ErrorCode::NO_ERROR); + + std::unique_ptr nfmt; + if (kNumberTypeCurrency == type) { + nfmt.reset(NumberFormat::createCurrencyInstance(l, ec)); + if (U_ZERO_ERROR >= ec) { + ParsePosition ppos; + std::unique_ptr ca(nfmt->parseCurrency(str, ppos)); + // always use functions with error check + if (!ca) { + LoggerE("parseCurrency failed"); + res = PlatformResult(ErrorCode::UNKNOWN_ERR, "parseCurrency failed"); + } else { + *result = ca->getNumber().getDouble(ec); + if (U_ZERO_ERROR < ec) { + res = PlatformResult(ErrorCode::UNKNOWN_ERR, "getting double value failed"); + } + } + } else { + res = PlatformResult(ErrorCode::UNKNOWN_ERR, "could not create currency parser"); + } + return res; + } else if (kNumberTypePercent == type) { + nfmt.reset(NumberFormat::createPercentInstance(l, ec)); + } else { + nfmt.reset(NumberFormat::createInstance(l, ec)); + } + if (U_ZERO_ERROR < ec) { + LoggerD("could not create number parser: %d", ec); + res = PlatformResult(ErrorCode::UNKNOWN_ERR, "could not create number parser"); + } else { + Formattable formatable; + nfmt->parse(str, formatable, ec); + if (U_ZERO_ERROR >= ec) { + *result = formatable.getDouble(ec); + if (U_ZERO_ERROR < ec) { + res = PlatformResult(ErrorCode::UNKNOWN_ERR, "getting double value failed"); + } + } else { + res = PlatformResult(ErrorCode::UNKNOWN_ERR, "parsing failed"); + } + } + return res; +} + +PlatformResult CordovaGlobalizationTools::GetNumberPattern(const std::string& type, std::string* pattern, + std::string* symbol, double* fraction, + double* rounding, std::string* positive, + std::string* negative, std::string* decimal, + std::string* grouping) { + LoggerD("Entered"); + UErrorCode ec = U_ZERO_ERROR; + UnicodeString res; + Locale l = GetDefaultLocale(); + + std::unique_ptr nfmt; + icu::DecimalFormatSymbols dfs = DecimalFormatSymbols(l, ec); + if (kNumberTypeCurrency == type) { + nfmt.reset(NumberFormat::createCurrencyInstance(l, ec)); + *symbol = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kCurrencySymbol)); + } else if (kNumberTypePercent == type) { + nfmt.reset(NumberFormat::createPercentInstance(l, ec)); + *symbol = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol)); + } else { + nfmt.reset(NumberFormat::createInstance(l, ec)); + *symbol = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); + } + if (U_ZERO_ERROR >= ec) { + if (DecimalFormat::getStaticClassID() != nfmt->getDynamicClassID()) { + LoggerE("Could not cast to DecimalFormat, operation failed"); + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not cast to DecimalFormat"); + } else { + LoggerE("Casting to DecimalFormat is allowed"); + } + DecimalFormat* df = dynamic_cast(nfmt.get()); + if (!df) { + LoggerE("Casting failed"); + *pattern = ""; + } + *pattern = ToUTF8String(df->toLocalizedPattern(res)); + *fraction = df->getMaximumFractionDigits(); + *rounding = df->getRoundingIncrement(); + *positive = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kPlusSignSymbol)); + *negative = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kMinusSignSymbol)); + *decimal = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); + *grouping = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); + + return PlatformResult(ErrorCode::NO_ERROR); + } else { + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not get number pattern"); + } +} + +PlatformResult CordovaGlobalizationTools::GetCurrencyPattern(const std::string& code, std::string* pattern, + double* fraction, double* rounding, + std::string* decimal, std::string* grouping) { + LoggerD("Entered"); + UErrorCode ec = U_ZERO_ERROR; + UnicodeString res; + Locale l = GetDefaultLocale(); + + if (code.length() >= 3) { + std::unique_ptr nfmt(NumberFormat::createCurrencyInstance(l, ec)); + if (U_ZERO_ERROR >= ec) { + UChar currency[3] = {code[0], code[1], code[2]}; + nfmt->setCurrency(currency,ec); + if (U_ZERO_ERROR >= ec) { + icu::DecimalFormatSymbols dfs = DecimalFormatSymbols(l, ec); + if (U_ZERO_ERROR >= ec) { + if (DecimalFormat::getStaticClassID() != nfmt->getDynamicClassID()) { + LoggerE("Could not cast to DecimalFormat, operation failed"); + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not cast to DecimalFormat"); + } else { + LoggerE("Casting to DecimalFormat is allowed"); + } + DecimalFormat* df = dynamic_cast(nfmt.get()); + if (df) { + *pattern = ToUTF8String(df->toLocalizedPattern(res)); + + // find currency symbol + UnicodeString str; + nfmt->format(123, str); // use fake value just to get currency symbol + std::string formatted_currency = ToUTF8String(str); + std::string currency_symbol = formatted_currency.substr(0, formatted_currency.find("123")); + LoggerD("currency_symbol %s", currency_symbol.c_str()); + + // replacing stub given from platform with correct symbol + std::string currency_stub = "¤"; // core API returns it instead of currency symbol + size_t found_pos = pattern->find(currency_stub, 0); + while (string::npos != found_pos) { + pattern->replace(found_pos, currency_stub.length(), currency_symbol); + found_pos = pattern->find(currency_stub, found_pos); + } + + LoggerD("new pattern %s", pattern->c_str()); + *fraction = df->getMaximumFractionDigits(); + *rounding = df->getRoundingIncrement(); + *decimal = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); + *grouping = ToUTF8String(dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); + return PlatformResult(ErrorCode::NO_ERROR); + } + } + } + } + } + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Could not get currency pattern"); +} + +} // globalization +} // cordova +} // extension diff --git a/src/globalization/cordova_globalization_tools.h b/src/globalization/cordova_globalization_tools.h new file mode 100644 index 0000000..9703d55 --- /dev/null +++ b/src/globalization/cordova_globalization_tools.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLOBALIZATION_CORDOVA_GLOBALIZATION_TOOLS_H_ +#define GLOBALIZATION_CORDOVA_GLOBALIZATION_TOOLS_H_ + +#include +#include +#include +#include +#include +#include +#include + + +namespace extension { +namespace cordova { +namespace globalization { + +extern const std::string kSelectorDateStr; +extern const std::string kSelectorTimeStr; + +extern const std::string kFormatShortStr; +extern const std::string kFormatMediumStr; +extern const std::string kFormatLongStr; +extern const std::string kFormatFullStr; + +extern const std::string kItemMonths; +extern const std::string kItemDays; + +extern const std::string kTypeWide; +extern const std::string kTypeNarrow; + +extern const std::string kNumberTypeDecimal; +extern const std::string kNumberTypePercent; +extern const std::string kNumberTypeCurrency; + +class CordovaGlobalizationTools{ + public: + static Locale GetDefaultLocale(); + static std::string ToUTF8String(const UnicodeString& uni_str); + static std::unique_ptr GetDateFormatPtr(DateFormat::EStyle format, + const std::string& selector); + static DateFormat::EStyle GetDateFormat(const std::string& length); + static std::string GetDateString(UDate date, DateFormat::EStyle format, + const std::string& selector); + static common::PlatformResult GetUDateFromString(const std::string& date, + DateFormat::EStyle format, + const std::string& selector, + UDate* result); + static common::PlatformResult GetDatePattern(DateFormat::EStyle format, + const std::string& selector, + std::string* result); + static common::PlatformResult GetNames(const std::string& item, const std::string& type, + std::vector* result); + static common::PlatformResult GetFirstDayOfWeek(double* result); + static common::PlatformResult FormatNumber(double number, const std::string& type, + std::string* result); + static common::PlatformResult ParseNumber(const std::string& number_str, const std::string& type, + double* result); + static common::PlatformResult GetNumberPattern(const std::string& type, std::string* pattern, + std::string* symbol, double* fraction, + double* rounding, std::string* positive, + std::string* negative, std::string* decimal, + std::string* grouping); + static common::PlatformResult GetCurrencyPattern(const std::string& code, std::string* pattern, + double* fraction, double* rounding, + std::string* decimal, std::string* grouping); +}; +} // globalization +} // cordova +} // extension + +#endif // GLOBALIZATION_CORDOVA_GLOBALIZATION_TOOLS_H_ -- 2.7.4