From 490d50e710c8f32dd51bb85281d9fe3bdfb7e41f Mon Sep 17 00:00:00 2001 From: Michal Michalski Date: Thu, 4 Jul 2019 16:16:05 +0200 Subject: [PATCH] [common][tizen][utils] Common Bundle implementation. http://suprem.sec.samsung.net/jira/browse/TWDAPI-225 Unified the way native bundle objects are used by WebAPI plugins. [Verification] unittests pass rate 100%. Signed-off-by: Michal Michalski Change-Id: I501880daee29f878e4af9fb9b20e386905fbdc0c --- src/common/json-utils.cc | 184 +++++++++++++++++++++++++++++++++++------------ src/common/json-utils.h | 9 +-- src/tizen/tizen_api.js | 91 ++++++++++++++++++++++- src/utils/utils_api.js | 33 ++++++++- 4 files changed, 263 insertions(+), 54 deletions(-) diff --git a/src/common/json-utils.cc b/src/common/json-utils.cc index 03e448f..a21aaee 100644 --- a/src/common/json-utils.cc +++ b/src/common/json-utils.cc @@ -14,77 +14,174 @@ * limitations under the License. */ +#include "json-utils.h" #include #include #include "common/logger.h" #include "common/picojson.h" -#include "json-utils.h" +#include -namespace common { +namespace { +using Octet = unsigned char; -PlatformResult JsonToBundle(const picojson::object& json, bundle* bundle_data) { +bool IsOctet(double value) { + return 0 <= value && value <= 255; +} + +int BundleAddString(const std::string& key, const picojson::value& value, bundle* bundle_data) { ScopeLogger(); - for (const auto& entry: json) { - int ret = bundle_add(bundle_data, entry.first.c_str(), entry.second.serialize().c_str()); - if (BUNDLE_ERROR_NONE != ret) { - return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error occured during json to bundle conversion", - ("bundle_add() returned error code: %d, error msg: %s", ret, get_error_message(ret))); - } + return bundle_add_str(bundle_data, key.c_str(), value.get().c_str()); +} + +std::vector JsonArrayToByteStream(const picojson::array& jarray) { + ScopeLogger(); + std::vector bytes; + bytes.reserve(jarray.size()); + + for (const auto& elem : jarray) { + bytes.push_back(static_cast(elem.get())); } - return PlatformResult(ErrorCode::NO_ERROR); + + return bytes; } -PlatformResult BundleToJson(bundle* bundle_data, picojson::object* json) { - picojson::array array_data; - bundle_foreach(bundle_data, BundleJsonIterator, &array_data); +int BundleAddByteStream(const std::string& key, const picojson::value& value, bundle* bundle_data) { + ScopeLogger(); + const picojson::array& jarray = value.get(); + auto bytes = JsonArrayToByteStream(jarray); + return bundle_add_byte(bundle_data, key.c_str(), bytes.data(), bytes.size()); +} - for (auto& elem: array_data) { - (*json)[elem.get("key").get()] = elem.get("value"); - } +int BundleAddStringArray(const std::string& key, const picojson::value& value, + bundle* bundle_data) { + ScopeLogger(); + const auto& jarray = value.get(); + + std::vector cstrings; + cstrings.reserve(jarray.size()); - return PlatformResult(ErrorCode::NO_ERROR); + for (const auto& elem : jarray) { + cstrings.push_back(elem.get().c_str()); + } + + return bundle_add_str_array(bundle_data, key.c_str(), cstrings.data(), cstrings.size()); } -PlatformResult JsonToBundle(const picojson::array& json, bundle* bundle_data) { +int BundleAddByteStreamArray(const std::string& key, const picojson::value& value, bundle* bundle_data) { ScopeLogger(); - for (const auto& item: json) { - if (!item.is()) { - return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Invalid json structure", - ("item.is() returned false")); - } - const auto& json = item.get(); + const auto& jarray = value.get(); - if (json.find("key") == json.end()) { - return LogAndCreateResult(ErrorCode::VALIDATION_ERR, "Key attribute is missing", - ("Key attribute not found in json object.")); - } - if (!json.at("key").is()) { - return LogAndCreateResult(ErrorCode::VALIDATION_ERR, "Key attribute needs to have string type", - ("Key attribute is not a string. Key value: %s", json.at("key").serialize().c_str())); + std::vector> bytes_array; + bytes_array.reserve(jarray.size()); + + for (const auto& elem : jarray) { + bytes_array.push_back(JsonArrayToByteStream(elem.get())); + } + + int ret = bundle_add_byte_array(bundle_data, key.c_str(), nullptr, bytes_array.size()); + if (BUNDLE_ERROR_NONE != ret) { + return ret; + } + + for (unsigned int idx = 0; idx < bytes_array.size(); ++idx) { + ret = bundle_set_byte_array_element(bundle_data, key.c_str(), idx, bytes_array[idx].data(), + bytes_array[idx].size()); + + if (BUNDLE_ERROR_NONE != ret) { + return ret; } - std::string key = json.at("key").get(); + } + + return BUNDLE_ERROR_NONE; +} - if (json.find("value") == json.end()) { - return LogAndCreateResult(ErrorCode::VALIDATION_ERR, "Value attribute is missing", - ("Value attribute not found in json object.")); +template +bool JsonValueTypeCheck(const picojson::value& value) { + return value.is(); +} + +template <> +bool JsonValueTypeCheck(const picojson::value& value) { + return JsonValueTypeCheck(value) && IsOctet(value.get()); +} + +bundle_type GetBundleType(const picojson::value& value) { + ScopeLogger(); + if (JsonValueTypeCheck(value)) return BUNDLE_TYPE_STR; + + if (!JsonValueTypeCheck(value)) return BUNDLE_TYPE_ANY; + const auto& jarray = value.get(); + + if (std::all_of(jarray.begin(), jarray.end(), JsonValueTypeCheck)) { + return BUNDLE_TYPE_STR_ARRAY; + } + + if (std::all_of(jarray.begin(), jarray.end(), JsonValueTypeCheck)) { + return BUNDLE_TYPE_BYTE; + } + + for (const auto& elem : jarray) { + if (!elem.is()) { + return BUNDLE_TYPE_ANY; } - if (!json.at("value").is()) { - return LogAndCreateResult(ErrorCode::VALIDATION_ERR, "Value attribute needs to have string type", - ("Value attribute is not a string. Value: %s", json.at("value").serialize().c_str())); + + const auto& subarray = elem.get(); + if (!std::all_of(subarray.begin(), subarray.end(), JsonValueTypeCheck)) { + return BUNDLE_TYPE_ANY; } - std::string value = json.at("value").get(); + } + + return BUNDLE_TYPE_BYTE_ARRAY; +} + +int BundleAdd(const std::string& key, const picojson::value& value, bundle* bundle_data) { + ScopeLogger(); + switch (GetBundleType(value)) { + case BUNDLE_TYPE_STR: + return BundleAddString(key, value, bundle_data); + case BUNDLE_TYPE_STR_ARRAY: + return BundleAddStringArray(key, value, bundle_data); + case BUNDLE_TYPE_BYTE: + return BundleAddByteStream(key, value, bundle_data); + case BUNDLE_TYPE_BYTE_ARRAY: + return BundleAddByteStreamArray(key, value, bundle_data); + default: + LoggerE("Unsupported bundle value type."); + return BUNDLE_ERROR_INVALID_PARAMETER; + } + return BUNDLE_ERROR_NONE; +} - int ret = bundle_add(bundle_data, key.c_str(), value.c_str()); +} // namespace + +namespace common { + +PlatformResult JsonToBundle(const picojson::object& json, bundle* bundle_data) { + ScopeLogger(); + for (const auto& property : json) { + int ret = BundleAdd(property.first, property.second, bundle_data); if (BUNDLE_ERROR_NONE != ret) { - return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error occured during json to bundle conversion", - ("bundle_add() returned error code: %d, error msg: %s", ret, get_error_message(ret))); + LoggerE("BundleAdd failed with error message: %s", get_error_message(ret)); + return PlatformResult(ErrorCode::UNKNOWN_ERR); } } return PlatformResult(ErrorCode::NO_ERROR); } +PlatformResult BundleToJson(bundle* bundle_data, picojson::object* json) { + ScopeLogger(); + picojson::array array_data; + bundle_foreach(bundle_data, BundleJsonIterator, &array_data); + + for (auto& elem : array_data) { + (*json)[elem.get("key").get()] = elem.get("value"); + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + void BundleJsonIterator(const char* key, const int type, const bundle_keyval_t* kv, void* d) { ScopeLogger(); @@ -167,5 +264,4 @@ void BundleJsonIterator(const char* key, const int type, const bundle_keyval_t* array->push_back(picojson::value(o)); } -} // namespace common - +} // namespace common diff --git a/src/common/json-utils.h b/src/common/json-utils.h index a3d8df4..4579a8c 100644 --- a/src/common/json-utils.h +++ b/src/common/json-utils.h @@ -18,9 +18,8 @@ #define COMMON_JSON_UTILS_H #include -#include "platform_result.h" #include "picojson.h" - +#include "platform_result.h" namespace common { @@ -28,9 +27,7 @@ void BundleJsonIterator(const char* key, const int type, const bundle_keyval_t* PlatformResult JsonToBundle(const picojson::object& json, bundle* bundle_data); PlatformResult BundleToJson(bundle* bundle_data, picojson::object* json); -PlatformResult JsonToBundle(const picojson::array& json, bundle* bundle_data); - -} // namespace common; +} // namespace common; -#endif // COMMON_JSON_UTILS_H +#endif // COMMON_JSON_UTILS_H diff --git a/src/tizen/tizen_api.js b/src/tizen/tizen_api.js index eaa502b..f1cd820 100644 --- a/src/tizen/tizen_api.js +++ b/src/tizen/tizen_api.js @@ -6,7 +6,6 @@ // Tizen API Specification: // https://developer.tizen.org/dev-guide/2.3.0/org.tizen.mobile.web.device.apireference/tizen/tizen.html - // WebAPIException and WebAPIError definition moved to src/utils/utils_api.js // for compliance reasons. You can find more info there. @@ -473,3 +472,93 @@ exports.SimpleCoordinates = function(lat, lng) { }); }; exports.SimpleCoordinates.prototype.constructor = exports.SimpleCoordinates; + + +function forEachOwnProperty(obj, callback) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + callback(prop, obj[prop]); + } + } +} + +var BundleValueType = { + STRING: 'STRING', + STRING_ARRAY: 'STRING_ARRAY', + BYTES: 'BYTES', + BYTES_ARRAY: 'BYTES_ARRAY' +}; +exports.BundleValueType = BundleValueType; + +function getValueType(value) { + if (xwalk.utils.type.isString(value)) { + return BundleValueType.STRING; + } + if (xwalk.utils.type.isStringArray(value)) { + return BundleValueType.STRING_ARRAY; + } + if (xwalk.utils.type.isByteStream(value)) { + return BundleValueType.BYTES; + } + if (xwalk.utils.type.isByteStreamArray(value)) { + return BundleValueType.BYTES_ARRAY; + } +} + +exports.Bundle = function(json) { + xwalk.utils.validator.isConstructorCall(this, exports.Bundle); + this.data = {}; + + if (xwalk.utils.validator.isObject(json)) { + forEachOwnProperty( + json, + function(key, value) { + this.set(key, value); + }.bind(this) + ); + } +}; +exports.Bundle.prototype.constructor = exports.Bundle; + +exports.Bundle.prototype.set = function(key, value) { + if (undefined === getValueType(value)) { + value = xwalk.utils.converter.toString(value); + } + this.data[key] = value; +}; + +exports.Bundle.prototype.get = function(key) { + if (!this.data.hasOwnProperty(key)) { + throw new WebAPIException(WebAPIException.NOT_FOUND_ERR); + } + return this.data[key]; +}; + +exports.Bundle.prototype.typeOf = function(key) { + if (!this.data.hasOwnProperty(key)) { + throw new WebAPIException(WebAPIException.NOT_FOUND_ERR); + } + return getValueType(this.data[key]); +}; + +exports.Bundle.prototype.forEach = function(callback) { + forEachOwnProperty( + this.data, + function(key, value) { + callback(key, value, this.typeOf(key)); + }.bind(this) + ); +}; + +exports.Bundle.prototype.toJSON = function() { + var json = {}; + this.forEach(function(key, value) { + json[key] = value; + }); + return json; +}; + +exports.Bundle.prototype.toString = function() { + return JSON.stringify(this.data); +}; + diff --git a/src/utils/utils_api.js b/src/utils/utils_api.js index b281ced..ddeb04c 100644 --- a/src/utils/utils_api.js +++ b/src/utils/utils_api.js @@ -384,7 +384,6 @@ Utils.prototype.printDeprecationWarningFor = function(name, replacement) { } }; - ///////////////////////////////////////////////////////////////////////////// /** @constructor */ var Type = function() {}; @@ -401,6 +400,31 @@ Type.prototype.isArray = function(obj) { return Array.isArray(obj); }; +Type.prototype.isOctet = function(value) { + return Number.isInteger(value) && 0 <= value && value <= 255; +}; + +Type.prototype.isByteStream = function(value) { + return value instanceof Uint8Array; +}; + +Type.prototype.isByteStreamArray = function(value) { + return Array.isArray(value) && value.every(this.isByteStream); +}; + +Type.prototype.isLegacyByteStream = function(value) { + return Array.isArray(value) && value.every(this.isOctet); +}; + +Type.prototype.isLegacyByteStreamArray = function(value) { + return ( + Array.isArray(value) && + value.every((function(x) { + return this.isLegacyByteStream(x); + }).bind(this)) + ); +}; + Type.prototype.isFunction = function(obj) { return typeof obj === 'function'; }; @@ -413,6 +437,11 @@ Type.prototype.isString = function(obj) { return typeof obj === 'string'; }; +Type.prototype.isStringArray = function(value) { + return Array.isArray(value) && + value.every(this.isString); +}; + Type.prototype.isDate = function(obj) { return obj instanceof Date; }; @@ -463,8 +492,6 @@ Type.prototype.getValues = function(obj) { var _type = new Type(); - - ///////////////////////////////////////////////////////////////////////////// /** @constructor */ var Converter = function() {}; -- 2.7.4