1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define LOG_TAG "NapiParser"
17 #include "napi_parser.h"
18
19 #include <charconv>
20 #include <iostream>
21 #include <sstream>
22
23 #include <nlohmann/json.hpp>
24
25 #include "napi_errno.h"
26 #include "napi_error_utils.h"
27 #include "napi_node.h"
28 #include "napi_utils.h"
29
30 namespace OHOS::CollaborationEdit {
31
32 static constexpr const uint8_t NUMBER_OF_FIELDS_IN_ID = 2;
33
34 static std::vector<std::string> g_cloudDbFields = {
35 "batchInsert",
36 "query",
37 "downloadAsset",
38 "uploadAsset",
39 "deleteAsset",
40 "deleteLocalAsset"
41 };
42
Stringsplit(std::string str,const char split,std::vector<std::string> & res)43 void Parser::Stringsplit(std::string str, const char split, std::vector<std::string> &res)
44 {
45 std::istringstream iss(str);
46 std::string token;
47 while (std::getline(iss, token, split)) {
48 res.push_back(token);
49 }
50 }
51
ConvertToUint64(std::string str,uint64_t & out)52 bool Parser::ConvertToUint64(std::string str, uint64_t &out)
53 {
54 auto [ptr, errCode] = std::from_chars(str.data(), str.data() + str.size(), out);
55 return errCode == std::errc{} && ptr == str.data() + str.size();
56 }
57
ConvertStrToID(std::string idStr)58 std::optional<ID> Parser::ConvertStrToID(std::string idStr)
59 {
60 if (idStr.empty()) {
61 return std::nullopt;
62 }
63 std::vector<std::string> strList;
64 Stringsplit(idStr, '_', strList);
65 if (strList.size() != NUMBER_OF_FIELDS_IN_ID) { // id returned from kernel is a string like 'id_clock'
66 return std::nullopt;
67 }
68 uint64_t clock = 0;
69 if (!ConvertToUint64(strList[1], clock)) {
70 return std::nullopt;
71 }
72 ID id(strList[0], clock);
73 return id;
74 }
75
ParseJsonToJsNode(napi_env env,json jsonObj,AbstractType * parent,napi_value & out)76 int ParseJsonToJsNode(napi_env env, json jsonObj, AbstractType *parent, napi_value &out)
77 {
78 napi_value constructor = Node::Constructor(env);
79 ASSERT(constructor != nullptr, "node constructor is null", ERR);
80 std::string nodeName = jsonObj["nodeName"];
81 std::string idStr = jsonObj["elementId"];
82 std::optional<ID> id = Parser::ConvertStrToID(idStr);
83 ASSERT(id.has_value(), "incorrect id.", ERR); // ID should not be null
84 napi_value jsNodeName = nullptr;
85 napi_status status = NapiUtils::SetValue(env, nodeName, jsNodeName);
86 ASSERT(status == napi_ok, "wrap nodeName go wrong.", ERR);
87 size_t argc = 1;
88 napi_value argv[1] = {jsNodeName};
89 napi_value jsNode;
90 status = napi_new_instance(env, constructor, argc, argv, &jsNode);
91 ASSERT(status == napi_ok, "new node instance go wrong.", ERR);
92 Node *tempNode = nullptr;
93 status = napi_unwrap(env, jsNode, reinterpret_cast<void **>(&tempNode));
94 ASSERT(status == napi_ok, "unwrap jsNode go wrong.", ERR);
95 tempNode->SetDBStore(parent->GetDBStore());
96 tempNode->SetTableName(parent->GetTableName());
97 tempNode->SetID(id);
98 out = jsNode;
99 return OK;
100 }
101
ParseJsonToJsText(napi_env env,json jsonObj,AbstractType * parent,napi_value & out)102 int ParseJsonToJsText(napi_env env, json jsonObj, AbstractType *parent, napi_value &out)
103 {
104 napi_value constructor = Text::Constructor(env);
105 ASSERT(constructor != nullptr, "text constructor is null", ERR);
106 std::string idStr = jsonObj["elementId"];
107 LOG_DEBUG("id is %{public}s", idStr.c_str());
108 std::optional<ID> id = Parser::ConvertStrToID(idStr);
109 ASSERT(id.has_value(), "incorrect id.", ERR); // ID should not be null
110 napi_value jsText;
111 napi_status status = napi_new_instance(env, constructor, 0, nullptr, &jsText);
112 ASSERT(status == napi_ok, "new text instance go wrong.", ERR);
113 Node *tempText = nullptr;
114 status = napi_unwrap(env, jsText, reinterpret_cast<void **>(&tempText));
115 ASSERT(status == napi_ok, "unwrap jsText go wrong.", ERR);
116 tempText->SetDBStore(parent->GetDBStore());
117 tempText->SetTableName(parent->GetTableName());
118 tempText->SetID(id);
119 out = jsText;
120 return OK;
121 }
122
ParseJsonStrToJsChildren(napi_env env,const std::string & nodeJsonStr,AbstractType * parent,napi_value & out)123 int Parser::ParseJsonStrToJsChildren(
124 napi_env env, const std::string &nodeJsonStr, AbstractType *parent, napi_value &out)
125 {
126 ASSERT(!nodeJsonStr.empty() && json::accept(nodeJsonStr), "invalid json str", ERR);
127 napi_status status = napi_create_array(env, &out);
128 ASSERT(status == napi_ok, "create array go wrong!", ERR);
129 json jsonArray = json::parse(nodeJsonStr);
130 ASSERT(jsonArray.is_array(), "result is not json array.", ERR);
131 int i = 0;
132 for (const auto &jsonObj : jsonArray) {
133 if (!jsonObj.contains("type")) {
134 continue;
135 }
136 std::string type = jsonObj["type"];
137 napi_value jsNode;
138 if (type.compare("XML_ELEMENT") == 0) {
139 int ret = ParseJsonToJsNode(env, jsonObj, parent, jsNode);
140 ASSERT(ret == OK, "Parse json to node go wrong.", ERR);
141 } else if (type.compare("XML_TEXT") == 0) {
142 int ret = ParseJsonToJsText(env, jsonObj, parent, jsNode);
143 ASSERT(ret == OK, "Parse json to text go wrong.", ERR);
144 } else {
145 LOG_ERROR("Unsupported type. type = %{public}s", type.c_str());
146 continue;
147 }
148 status = napi_set_element(env, out, i, jsNode);
149 ASSERT(status == napi_ok, "set element go wrong.", ERR);
150 i++;
151 }
152 return OK;
153 }
154
ParseJsonStrToJsUpdateNode(napi_env env,std::string nodeJsonStr,std::shared_ptr<DBStore> dbStore,napi_value & out)155 int Parser::ParseJsonStrToJsUpdateNode(
156 napi_env env, std::string nodeJsonStr, std::shared_ptr<DBStore> dbStore, napi_value &out)
157 {
158 ASSERT(!nodeJsonStr.empty() && json::accept(nodeJsonStr), "invalid json str", ERR);
159 napi_status status = napi_create_array(env, &out);
160 ASSERT(status == napi_ok, "create array go wrong!", ERR);
161 json jsonArray = json::parse(nodeJsonStr);
162 ASSERT(jsonArray.is_array(), "result is not json array.", ERR);
163 int i = 0;
164 for (const auto &jsonObj : jsonArray) {
165 if (!jsonObj.contains("type") || !jsonObj.contains("name")) {
166 continue;
167 }
168 std::string type = jsonObj["type"];
169 std::string tableName = NapiUtils::RemovePrefix(jsonObj["name"], std::to_string(LABEL_FRAGMENT) + "_");
170 AbstractType parent;
171 parent.SetDBStore(dbStore);
172 parent.SetTableName(tableName);
173
174 napi_value jsNode;
175 if (type.compare("XML_ELEMENT") == 0) {
176 int ret = ParseJsonToJsNode(env, jsonObj, &parent, jsNode);
177 ASSERT(ret == OK, "Parse json to node go wrong.", ERR);
178 } else if (type.compare("XML_TEXT") == 0) {
179 int ret = ParseJsonToJsText(env, jsonObj, &parent, jsNode);
180 ASSERT(ret == OK, "Parse json to text go wrong.", ERR);
181 } else {
182 LOG_ERROR("Unsupported type. type = %{public}s", type.c_str());
183 continue;
184 }
185
186 napi_value jsUpdateNode = nullptr;
187 status = napi_create_object(env, &jsUpdateNode);
188 ASSERT(status == napi_ok, "create object go wrong!", ERR);
189 napi_value jstableName;
190 NapiUtils::SetValue(env, tableName, jstableName);
191 status = napi_set_named_property(env, jsUpdateNode, "editUnitName", jstableName);
192 ASSERT(status == napi_ok, "set editUnitName go wrong.", ERR);
193 status = napi_set_named_property(env, jsUpdateNode, "node", jsNode);
194 ASSERT(status == napi_ok, "set node go wrong.", ERR);
195 status = napi_set_element(env, out, i, jsUpdateNode);
196 ASSERT(status == napi_ok, "set element go wrong.", ERR);
197 i++;
198 }
199 return OK;
200 }
201
ParseFromAttrsJsonStr(napi_env env,const std::string & jsonStr,napi_value & out)202 int Parser::ParseFromAttrsJsonStr(napi_env env, const std::string &jsonStr, napi_value &out)
203 {
204 ASSERT(!jsonStr.empty() && json::accept(jsonStr), "invalid json str", ERR);
205 napi_status status = napi_create_object(env, &out);
206 ASSERT(status == napi_ok, "create object go wrong!", ERR);
207 json jsonObject = json::parse(jsonStr);
208 napi_value jsAttr = nullptr;
209 for (json::iterator iter = jsonObject.begin(); iter != jsonObject.end(); ++iter) {
210 std::string key = iter.key();
211 json value = iter.value();
212 if (value.is_string()) {
213 NapiUtils::SetValue(env, value.get<std::string>(), jsAttr);
214 } else if (value.is_number_integer()) {
215 NapiUtils::SetValue(env, value.get<int>(), jsAttr);
216 } else if (value.is_boolean()) {
217 NapiUtils::SetValue(env, value.get<bool>(), jsAttr);
218 } else {
219 LOG_ERROR("Unsupported value type");
220 continue;
221 }
222 status = napi_set_named_property(env, out, key.c_str(), jsAttr);
223 }
224 return OK;
225 }
226
ParseJsFormatToStr(napi_env env,napi_value jsFormat,std::string & out)227 int Parser::ParseJsFormatToStr(napi_env env, napi_value jsFormat, std::string &out)
228 {
229 napi_value keys = nullptr;
230 napi_get_all_property_names(env, jsFormat, napi_key_own_only,
231 static_cast<napi_key_filter>(napi_key_enumerable | napi_key_skip_symbols), napi_key_numbers_to_strings, &keys);
232 uint32_t arrLen = 0;
233 napi_status status = napi_get_array_length(env, keys, &arrLen);
234 ASSERT(status == napi_ok, "get keys array length go wrong.", ERR);
235 json resJson;
236 for (size_t i = 0; i < arrLen; i++) {
237 napi_value key = nullptr;
238 status = napi_get_element(env, keys, i, &key);
239 ASSERT(status == napi_ok, "get key element go wrong.", ERR);
240 std::string keyStr;
241 NapiUtils::GetValue(env, key, keyStr);
242 napi_value jsValue = nullptr;
243 napi_get_property(env, jsFormat, key, &jsValue);
244 std::string value;
245 int ret = ParseVariantJsValueToStr(env, jsValue, value);
246 ASSERT(ret == OK, "parse value go wrong.", ERR);
247 resJson[keyStr] = value;
248 }
249 out = resJson.dump();
250 return OK;
251 }
252
ParseVariantJsValueToStr(napi_env env,napi_value input,std::string & out)253 int Parser::ParseVariantJsValueToStr(napi_env env, napi_value input, std::string &out)
254 {
255 napi_valuetype valueType;
256 napi_status status = napi_typeof(env, input, &valueType);
257 ASSERT(status == napi_ok, "get type of input go wrong.", ERR);
258 ASSERT(valueType != napi_undefined, "cannot be undefined.", ERR);
259 switch (valueType) {
260 case napi_number: {
261 int64_t intValue;
262 status = NapiUtils::GetValue(env, input, intValue);
263 if (status == napi_ok) {
264 out = std::to_string(intValue);
265 break;
266 }
267 double doubleValue;
268 status = NapiUtils::GetValue(env, input, doubleValue);
269 ASSERT(status == napi_ok, "Neither int or double", ERR);
270 out = std::to_string(doubleValue);
271 break;
272 }
273 case napi_string: {
274 std::string strValue;
275 status = NapiUtils::GetValue(env, input, strValue);
276 ASSERT(status == napi_ok, "convert to str go wrong.", ERR);
277 out = strValue;
278 break;
279 }
280 case napi_boolean: {
281 bool bValue;
282 status = NapiUtils::GetValue(env, input, bValue);
283 ASSERT(status == napi_ok, "convert to bool go wrong.", ERR);
284 out = bValue ? "true" : "false";
285 break;
286 }
287 default: {
288 LOG_ERROR("unsupported format type: %{public}d", valueType);
289 return ERR;
290 }
291 }
292 return OK;
293 }
294
CheckValueType(napi_env env,napi_value value)295 int Parser::CheckValueType(napi_env env, napi_value value)
296 {
297 napi_valuetype valueType;
298 napi_status status = napi_typeof(env, value, &valueType);
299 if (status != napi_ok) {
300 LOG_ERROR("type of args go wrong, status = %{public}d", status);
301 return ERR;
302 }
303 if (valueType != napi_function) {
304 LOG_ERROR("value type go wrong: %{public}d", valueType);
305 return ERR;
306 }
307 return OK;
308 }
309
ParseCloudDbFields(napi_env env,napi_value input,std::vector<napi_value> & cloudDbFuncVector)310 int Parser::ParseCloudDbFields(napi_env env, napi_value input, std::vector<napi_value> &cloudDbFuncVector)
311 {
312 for (auto field : g_cloudDbFields) {
313 napi_value cloudDbFunc = nullptr;
314 napi_status status = NapiUtils::GetNamedProperty(env, input, field.c_str(), cloudDbFunc);
315 if (status != napi_ok) {
316 LOG_ERROR("get func go wrong, status = %{public}d", status);
317 return ERR;
318 }
319 int ret = CheckValueType(env, cloudDbFunc);
320 if (ret != OK) {
321 LOG_ERROR("check func type go wrong, status = %{public}d", ret);
322 return ret;
323 }
324 cloudDbFuncVector.push_back(cloudDbFunc);
325 }
326 return OK;
327 }
328
GetUniqueIdFromJsonStr(napi_env env,json & root)329 napi_value Parser::GetUniqueIdFromJsonStr(napi_env env, json &root)
330 {
331 ASSERT(root.contains("client"), "parse client from json str go wrong", nullptr);
332 ASSERT(root.contains("clock"), "parse clock from json str go wrong", nullptr);
333
334 napi_value uniqueId = nullptr;
335 NAPI_CALL(env, napi_create_object(env, &uniqueId));
336 napi_value jsDeviceId = nullptr;
337 std::string client = root["client"];
338 NapiUtils::SetValue(env, client, jsDeviceId);
339 NAPI_CALL(env, napi_set_named_property(env, uniqueId, "id", jsDeviceId));
340 napi_value jsClock = nullptr;
341 int64_t clock = root["clock"];
342 NapiUtils::SetValue(env, clock, jsClock);
343 NAPI_CALL(env, napi_set_named_property(env, uniqueId, "clock", jsClock));
344
345 return uniqueId;
346 }
347
SetRelativePosType(napi_env env,json & root,napi_value & relativePos)348 int Parser::SetRelativePosType(napi_env env, json &root, napi_value &relativePos)
349 {
350 if (!root.contains("type")) {
351 return OK;
352 }
353 napi_value jsType = Parser::GetUniqueIdFromJsonStr(env, root["type"]);
354 if (jsType == nullptr) {
355 return ERR;
356 }
357 napi_status status = napi_set_named_property(env, relativePos, "parentId", jsType);
358 return status == napi_ok ? OK : ERR;
359 }
360
SetRelativePosItem(napi_env env,json & root,napi_value & relativePos)361 int Parser::SetRelativePosItem(napi_env env, json &root, napi_value &relativePos)
362 {
363 if (!root.contains("item")) {
364 return OK;
365 }
366 napi_value jsItem = Parser::GetUniqueIdFromJsonStr(env, root["item"]);
367 if (jsItem == nullptr) {
368 return ERR;
369 }
370 napi_status status = napi_set_named_property(env, relativePos, "id", jsItem);
371 return status == napi_ok ? OK : ERR;
372 }
373
SetRelativePosTname(napi_env env,json & root,napi_value & relativePos)374 int Parser::SetRelativePosTname(napi_env env, json &root, napi_value &relativePos)
375 {
376 if (!root.contains("tname")) {
377 return OK;
378 }
379 std::string tnameStrTmp = root["tname"];
380 auto tnameStr = NapiUtils::RemovePrefix(tnameStrTmp, std::to_string(LABEL_FRAGMENT) + "_");
381 napi_value tname = nullptr;
382 NapiUtils::SetValue(env, tnameStr, tname);
383 napi_status status = napi_set_named_property(env, relativePos, "parentName", tname);
384 return status == napi_ok ? OK : ERR;
385 }
386
SetRelativePosAssoc(napi_env env,json & root,napi_value & relativePos)387 int Parser::SetRelativePosAssoc(napi_env env, json &root, napi_value &relativePos)
388 {
389 if (!root.contains("assoc")) {
390 return OK;
391 }
392 napi_value assoc;
393 int64_t assoc_num = root["assoc"];
394 NapiUtils::SetValue(env, assoc_num, assoc);
395 napi_status status = napi_set_named_property(env, relativePos, "pos", assoc);
396 return status == napi_ok ? OK : ERR;
397 }
398
GetRelativePosFromJsonStr(napi_env env,std::string & relPos)399 napi_value Parser::GetRelativePosFromJsonStr(napi_env env, std::string &relPos)
400 {
401 ASSERT_THROW(env, json::accept(relPos), Status::INTERNAL_ERROR, "parse relpos str go wrong");
402 json root = json::parse(relPos);
403
404 napi_value relativePos;
405 NAPI_CALL(env, napi_create_object(env, &relativePos));
406 Parser::SetRelativePosType(env, root, relativePos);
407 Parser::SetRelativePosItem(env, root, relativePos);
408 Parser::SetRelativePosTname(env, root, relativePos);
409 Parser::SetRelativePosAssoc(env, root, relativePos);
410 return relativePos;
411 }
412
GetUniqueIdFromNapiValueToJsonStr(napi_env env,napi_value type,json & typeJson)413 void Parser::GetUniqueIdFromNapiValueToJsonStr(napi_env env, napi_value type, json &typeJson)
414 {
415 std::string id;
416 int64_t clock;
417 napi_status status = NapiUtils::GetNamedProperty(env, type, "id", id);
418 ASSERT_THROW_VOID(env, status == napi_ok, Status::INVALID_ARGUMENT, "read id param from type go wrong");
419 status = NapiUtils::GetNamedProperty(env, type, "clock", clock);
420 ASSERT_THROW_VOID(env, status == napi_ok, Status::INVALID_ARGUMENT, "read clock param from type go wrong");
421 typeJson["client"] = id;
422 typeJson["clock"] = clock;
423 }
424
ParseFromAssetOpConfig(napi_env env,const AssetOpConfig & config)425 napi_value Parser::ParseFromAssetOpConfig(napi_env env, const AssetOpConfig &config)
426 {
427 napi_value jsConfig = nullptr;
428 NAPI_CALL(env, napi_create_object(env, &jsConfig));
429 napi_value jsPath = nullptr;
430 NapiUtils::SetValue(env, config.inputPath, jsPath);
431 NAPI_CALL(env, napi_set_named_property(env, jsConfig, "path", jsPath));
432 return jsConfig;
433 }
434
435 } // namespace OHOS::CollaborationEdit
436