[common][tizen][utils] Common Bundle implementation. 29/207729/21
authorMichal Michalski <m.michalski2@partner.samsung.com>
Thu, 4 Jul 2019 14:16:05 +0000 (16:16 +0200)
committerMichal Michalski <m.michalski2@partner.samsung.com>
Mon, 8 Jul 2019 10:03:28 +0000 (12:03 +0200)
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 <m.michalski2@partner.samsung.com>
Change-Id: I501880daee29f878e4af9fb9b20e386905fbdc0c

src/common/json-utils.cc
src/common/json-utils.h
src/tizen/tizen_api.js
src/utils/utils_api.js

index 03e448f..a21aaee 100644 (file)
  *    limitations under the License.
  */
 
+#include "json-utils.h"
 #include <bundle.h>
 #include <bundle_internal.h>
 #include "common/logger.h"
 #include "common/picojson.h"
-#include "json-utils.h"
 
+#include <algorithm>
 
-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<std::string>().c_str());
+}
+
+std::vector<Octet> JsonArrayToByteStream(const picojson::array& jarray) {
+  ScopeLogger();
+  std::vector<Octet> bytes;
+  bytes.reserve(jarray.size());
+
+  for (const auto& elem : jarray) {
+    bytes.push_back(static_cast<Octet>(elem.get<double>()));
   }
-  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<picojson::array>();
+  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<std::string>()] = elem.get("value");
-    }
+int BundleAddStringArray(const std::string& key, const picojson::value& value,
+                         bundle* bundle_data) {
+  ScopeLogger();
+  const auto& jarray = value.get<picojson::array>();
+
+  std::vector<const char*> cstrings;
+  cstrings.reserve(jarray.size());
 
-    return PlatformResult(ErrorCode::NO_ERROR);
+  for (const auto& elem : jarray) {
+    cstrings.push_back(elem.get<std::string>().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<picojson::object>()) {
-      return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Invalid json structure",
-        ("item.is<picojson::object>() returned false"));
-    }
-    const auto& json = item.get<picojson::object>();
+  const auto& jarray = value.get<picojson::array>();
 
-    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<std::string>()) {
-      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<std::vector<Octet>> bytes_array;
+  bytes_array.reserve(jarray.size());
+
+  for (const auto& elem : jarray) {
+    bytes_array.push_back(JsonArrayToByteStream(elem.get<picojson::array>()));
+  }
+
+  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<std::string>();
+  }
+
+  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 <typename Type>
+bool JsonValueTypeCheck(const picojson::value& value) {
+  return value.is<Type>();
+}
+
+template <>
+bool JsonValueTypeCheck<Octet>(const picojson::value& value) {
+  return JsonValueTypeCheck<double>(value) && IsOctet(value.get<double>());
+}
+
+bundle_type GetBundleType(const picojson::value& value) {
+  ScopeLogger();
+  if (JsonValueTypeCheck<std::string>(value)) return BUNDLE_TYPE_STR;
+
+  if (!JsonValueTypeCheck<picojson::array>(value)) return BUNDLE_TYPE_ANY;
+  const auto& jarray = value.get<picojson::array>();
+
+  if (std::all_of(jarray.begin(), jarray.end(), JsonValueTypeCheck<std::string>)) {
+    return BUNDLE_TYPE_STR_ARRAY;
+  }
+
+  if (std::all_of(jarray.begin(), jarray.end(), JsonValueTypeCheck<Octet>)) {
+    return BUNDLE_TYPE_BYTE;
+  }
+
+  for (const auto& elem : jarray) {
+    if (!elem.is<picojson::array>()) {
+      return BUNDLE_TYPE_ANY;
     }
-    if (!json.at("value").is<std::string>()) {
-      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<picojson::array>();
+    if (!std::all_of(subarray.begin(), subarray.end(), JsonValueTypeCheck<Octet>)) {
+      return BUNDLE_TYPE_ANY;
     }
-    std::string value = json.at("value").get<std::string>();
+  }
+
+  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<std::string>()] = 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
index a3d8df4..4579a8c 100644 (file)
@@ -18,9 +18,8 @@
 #define COMMON_JSON_UTILS_H
 
 #include <bundle.h>
-#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
index eaa502b..f1cd820 100644 (file)
@@ -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);
+};
+
index b281ced..ddeb04c 100644 (file)
@@ -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() {};