[Cordova][Globalization] Added C++ layer for missing functionalities.
authorPiotr Kosko <p.kosko@samsung.com>
Tue, 27 Oct 2015 10:05:17 +0000 (11:05 +0100)
committerPiotr Kosko <p.kosko@samsung.com>
Tue, 3 Nov 2015 06:51:39 +0000 (07:51 +0100)
[Verification] All globalization tests pass

Change-Id: Iaeade03161404f6e8510dd100b792c136d96bfa4
Signed-off-by: Piotr Kosko <p.kosko@samsung.com>
src/globalization/cordova_globalization.gyp
src/globalization/cordova_globalization_api.js
src/globalization/cordova_globalization_extension.cc
src/globalization/cordova_globalization_extension.h
src/globalization/cordova_globalization_instance.cc [new file with mode: 0644]
src/globalization/cordova_globalization_instance.h [new file with mode: 0644]
src/globalization/cordova_globalization_tools.cc [new file with mode: 0644]
src/globalization/cordova_globalization_tools.h [new file with mode: 0644]

index 9c44927..82599f6 100644 (file)
         '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'
         ],
       },
     },
index e857ba9..d15fee5 100755 (executable)
@@ -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;
index 7bb780d..ddf1d3d 100755 (executable)
  */
 
 #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
index 91a37ec..cf053aa 100755 (executable)
@@ -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 (file)
index 0000000..721039a
--- /dev/null
@@ -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 <stdlib.h>
+#include <algorithm>
+#include <memory>
+#include <sstream>
+#include <common/logger.h>
+#include <common/picojson.h>
+#include <common/platform_result.h>
+#include <common/task-queue.h>
+#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<double>();
+
+  const std::string& format_length = args.get("formatLength").get<std::string>();
+  const std::string& selector = args.get("selector").get<std::string>();
+  const std::string& timestamp_str = args.get("timestamp").get<std::string>();
+  UDate date = std::stod(timestamp_str);
+
+  auto get = [this, format_length, selector, date](const std::shared_ptr<picojson::value>& 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<picojson::object>();
+    result_obj.insert(std::make_pair("value", picojson::value(result_str)));
+    ReportSuccess(result, response->get<picojson::object>());
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+
+  const std::string& format_length = args.get("formatLength").get<std::string>();
+  const std::string& selector = args.get("selector").get<std::string>();
+  const std::string& date_str = args.get("dateString").get<std::string>();
+
+  auto get = [this, format_length, selector, date_str](const std::shared_ptr<picojson::value>& 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<picojson::object>();
+      result_obj.insert(std::make_pair(
+          "year", picojson::value(static_cast<double>(result_time->tm_year + 1900))));
+      result_obj.insert(std::make_pair(
+          "month", picojson::value(static_cast<double>(result_time->tm_mon))));
+      result_obj.insert(std::make_pair(
+          "day", picojson::value(static_cast<double>(result_time->tm_mday))));
+      result_obj.insert(std::make_pair(
+          "hour", picojson::value(static_cast<double>(result_time->tm_hour))));
+      result_obj.insert(std::make_pair(
+          "minute", picojson::value(static_cast<double>(result_time->tm_min))));
+      result_obj.insert(std::make_pair(
+          "second", picojson::value(static_cast<double>(result_time->tm_sec))));
+      result_obj.insert(std::make_pair(
+          "millisecond", picojson::value(static_cast<double>(0.0))));
+
+      ReportSuccess(result, response->get<picojson::object>());
+    } else {
+      ReportError(ret, &(response->get<picojson::object>()));
+    }
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+
+  const std::string& format_length = args.get("formatLength").get<std::string>();
+  const std::string& selector = args.get("selector").get<std::string>();
+
+  auto get = [this, format_length, selector](const std::shared_ptr<picojson::value>& 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<picojson::object>();
+
+      // 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<picojson::object>());
+    } else {
+      ReportError(ret, &(response->get<picojson::object>()));
+    }
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+
+  const std::string& type = args.get("type").get<std::string>();
+  const std::string& item = args.get("item").get<std::string>();
+
+  auto get = [this, type, item](const std::shared_ptr<picojson::value>& response) -> void {
+    std::vector<std::string> 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<picojson::object>()));
+    }
+
+    // creating json array
+    picojson::value result_array = picojson::value(picojson::array());
+    picojson::array& array_obj = result_array.get<picojson::array>();
+    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<picojson::object>();
+    result_obj.insert(std::make_pair("value", result_array));
+    ReportSuccess(result, response->get<picojson::object>());
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+
+  auto get = [this](const std::shared_ptr<picojson::value>& 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<picojson::object>();
+      result_obj.insert(std::make_pair("value", picojson::value(first_day_value)));
+      ReportSuccess(result, response->get<picojson::object>());
+    } else {
+      ReportError(ret, &(response->get<picojson::object>()));
+    }
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+
+  // 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<std::string>());
+  istr >> number;
+
+  const std::string& type = args.get("type").get<std::string>();
+
+  auto get = [this, type, number](const std::shared_ptr<picojson::value>& 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<picojson::object>();
+      result_obj.insert(std::make_pair("value", picojson::value(result_str)));
+      ReportSuccess(result, response->get<picojson::object>());
+    } else {
+      ReportError(ret, &(response->get<picojson::object>()));
+    }
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+  const std::string& number_str =  args.get("number").get<std::string>();
+  const std::string& type = args.get("type").get<std::string>();
+
+  auto get = [this, type, number_str](const std::shared_ptr<picojson::value>& 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<picojson::object>();
+      result_obj.insert(std::make_pair("value", picojson::value(result_str)));
+      ReportSuccess(result, response->get<picojson::object>());
+    } else {
+      ReportError(ret, &(response->get<picojson::object>()));
+    }
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+  const std::string& type = args.get("type").get<std::string>();
+
+  auto get = [this, type](const std::shared_ptr<picojson::value>& response) -> void {
+    picojson::value result = picojson::value(picojson::object());
+    picojson::object& result_obj = result.get<picojson::object>();
+
+    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<picojson::object>());
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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<double>();
+  const std::string& code = args.get("currencyCode").get<std::string>();
+
+  auto get = [this, code](const std::shared_ptr<picojson::value>& response) -> void {
+    picojson::value result = picojson::value(picojson::object());
+    picojson::object& result_obj = result.get<picojson::object>();
+
+    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<picojson::object>());
+  };
+
+  auto get_response = [this, callback_id](const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Getting response");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair("callbackId", picojson::value{static_cast<double>(callback_id)}));
+    LoggerD("message: %s", response->serialize().c_str());
+    Instance::PostMessage(this, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+  TaskQueue::GetInstance().Queue<picojson::value>(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 (file)
index 0000000..d4fc780
--- /dev/null
@@ -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 <common/extension.h>
+#include <common/picojson.h>
+
+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 (file)
index 0000000..a42680c
--- /dev/null
@@ -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 <memory>
+#include <vconf.h>
+#include <unicode/dtfmtsym.h>
+#include <unicode/decimfmt.h>
+#include <common/logger.h>
+
+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<char, void(*)(void*)> result_buffer(static_cast<char*>(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<DateFormat> 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<DateFormat> 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<DateFormat> 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<DateFormat> 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<DateFormat> 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<SimpleDateFormat*>(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<std::string>* result) {
+  LoggerD("Entered");
+  std::vector<std::string> 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<UnicodeString*>(dfs.getMonths(count));
+      } else {
+        names_vector = const_cast<UnicodeString*>(dfs.getShortMonths(count));
+      }
+    } else {
+      if (kTypeWide == type) {
+        names_vector = const_cast<UnicodeString*>(dfs.getWeekdays(count));
+      } else {
+        names_vector = const_cast<UnicodeString*>(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<DateFormat> dfmt(DateFormat::createDateInstance(DateFormat::kFull,
+                                                                  GetDefaultLocale()));
+
+  UCalendarDaysOfWeek first_day = dfmt->getCalendar()->getFirstDayOfWeek(ec);
+  if (U_ZERO_ERROR >= ec) {
+    *result = static_cast<double>(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<NumberFormat> 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<NumberFormat> nfmt;
+  if (kNumberTypeCurrency == type) {
+    nfmt.reset(NumberFormat::createCurrencyInstance(l, ec));
+    if (U_ZERO_ERROR >= ec) {
+      ParsePosition ppos;
+      std::unique_ptr<CurrencyAmount> 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<NumberFormat> 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<DecimalFormat*>(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<NumberFormat> 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<DecimalFormat*>(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 (file)
index 0000000..9703d55
--- /dev/null
@@ -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 <string>
+#include <vector>
+#include <memory>
+#include <unicode/unistr.h>
+#include <unicode/smpdtfmt.h>
+#include <unicode/dtptngen.h>
+#include <common/platform_result.h>
+
+
+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<DateFormat> 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<std::string>* 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_