1/*
2 * Copyright (c) 2021-2023 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#include "napi_utils.h"
17#include "core/common/resource/resource_manager.h"
18
19namespace OHOS::Ace::Napi {
20using namespace OHOS::Ace;
21namespace {
22
23const std::regex RESOURCE_APP_STRING_PLACEHOLDER(R"(\%((\d+)(\$)){0,1}([dsf]))", std::regex::icase);
24constexpr int32_t NAPI_BUF_LENGTH = 256;
25constexpr int32_t UNKNOWN_RESOURCE_ID = -1;
26constexpr char BUNDLE_NAME[] = "bundleName";
27std::vector<std::string> RESOURCE_HEADS = { "app", "sys" };
28} // namespace
29
30static const std::unordered_map<int32_t, std::string> ERROR_CODE_TO_MSG {
31    { ERROR_CODE_PERMISSION_DENIED, "Permission denied. " },
32    { ERROR_CODE_PARAM_INVALID, "Parameter error. " },
33    { ERROR_CODE_SYSTEMCAP_ERROR, "Capability not supported. " },
34    { ERROR_CODE_INTERNAL_ERROR, "Internal error. " },
35    { ERROR_CODE_URI_ERROR, "Uri error. " },
36    { ERROR_CODE_PAGE_STACK_FULL, "Page stack error. " },
37    { ERROR_CODE_URI_ERROR_LITE, "Uri error. " },
38    { ERROR_CODE_DIALOG_CONTENT_ERROR, "Dialog content error. " },
39    { ERROR_CODE_DIALOG_CONTENT_ALREADY_EXIST, "Dialog content already exist. " },
40    { ERROR_CODE_DIALOG_CONTENT_NOT_FOUND, "Dialog content not found. " },
41    { ERROR_CODE_TOAST_NOT_FOUND, "Toast not found. " }
42};
43
44void NapiThrow(napi_env env, const std::string& message, int32_t errCode)
45{
46    napi_value code = nullptr;
47    std::string strCode = std::to_string(errCode);
48    napi_create_string_utf8(env, strCode.c_str(), strCode.length(), &code);
49
50    napi_value msg = nullptr;
51    auto iter = ERROR_CODE_TO_MSG.find(errCode);
52    std::string strMsg = (iter != ERROR_CODE_TO_MSG.end() ? iter->second : "") + message;
53    LOGE("napi throw errCode %d strMsg %s", errCode, strMsg.c_str());
54    napi_create_string_utf8(env, strMsg.c_str(), strMsg.length(), &msg);
55
56    napi_value error = nullptr;
57    napi_create_error(env, code, msg, &error);
58    napi_throw(env, error);
59}
60
61void ReplaceHolder(std::string& originStr, const std::vector<std::string>& params, uint32_t containCount)
62{
63    auto size = static_cast<uint32_t>(params.size());
64    if (containCount == size) {
65        return;
66    }
67    std::string::const_iterator start = originStr.begin();
68    std::string::const_iterator end = originStr.end();
69    std::smatch matches;
70    bool shortHolderType = false;
71    bool firstMatch = true;
72    uint32_t searchTime = 0;
73    while (std::regex_search(start, end, matches, RESOURCE_APP_STRING_PLACEHOLDER)) {
74        std::string pos = matches[2];
75        std::string type = matches[4];
76        if (firstMatch) {
77            firstMatch = false;
78            shortHolderType = pos.length() == 0;
79        } else {
80            if (static_cast<uint32_t>(shortHolderType) ^ ((uint32_t)(pos.length() == 0))) {
81                LOGE("wrong place holder,stop parse string");
82                return;
83            }
84        }
85
86        std::string replaceContentStr;
87        std::string::size_type index = 0;
88        if (shortHolderType) {
89            index = static_cast<std::string::size_type>(searchTime + containCount);
90        } else {
91            int32_t indexTmp = StringUtils::StringToInt(pos) + static_cast<int32_t>(containCount) - 1;
92            if (indexTmp >= 0) {
93                index = static_cast<std::string::size_type>(indexTmp);
94            } else {
95                LOGE("indexTmp err:%{public}d", indexTmp);
96            }
97        }
98        if (static_cast<uint32_t>(index) < size) {
99            replaceContentStr = params[index];
100        } else {
101            LOGE("index = %{public}d size = %{public}d", static_cast<uint32_t>(index), size);
102        }
103        originStr.replace(matches[0].first - originStr.begin(), matches[0].length(), replaceContentStr);
104        start = originStr.begin() + matches.prefix().length() + replaceContentStr.length();
105        end = originStr.end();
106        searchTime++;
107    }
108}
109
110size_t GetParamLen(napi_env env, napi_value param)
111{
112    size_t buffSize = 0;
113    napi_status status = napi_get_value_string_utf8(env, param, nullptr, 0, &buffSize);
114    if (status != napi_ok || buffSize == 0) {
115        return 0;
116    }
117    return buffSize;
118}
119
120bool NapiStringToString(napi_env env, napi_value value, std::string& retStr)
121{
122    size_t ret = 0;
123    napi_valuetype valueType = napi_undefined;
124    napi_typeof(env, value, &valueType);
125    if (valueType != napi_string) {
126        return false;
127    }
128    if (GetParamLen(env, value) == 0) {
129        return false;
130    }
131    size_t valueLen = GetParamLen(env, value) + 1;
132    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(valueLen);
133    napi_get_value_string_utf8(env, value, buffer.get(), valueLen, &ret);
134    retStr = buffer.get();
135    return true;
136}
137
138bool GetNapiString(napi_env env, napi_value value, std::string& retStr, napi_valuetype& valueType)
139{
140    if (NapiStringToString(env, value, retStr)) {
141        return true;
142    }
143    napi_typeof(env, value, &valueType);
144    if (valueType == napi_object) {
145        ResourceInfo recv;
146        if (ParseResourceParam(env, value, recv)) {
147            ParseString(recv, retStr);
148            return true;
149        }
150    }
151    return false;
152}
153
154RefPtr<ThemeConstants> GetThemeConstants(const std::optional<std::string>& bundleName = std::nullopt,
155    const std::optional<std::string>& moduleName = std::nullopt)
156{
157    auto container = Container::Current();
158    if (!container) {
159        LOGW("container is null");
160        return nullptr;
161    }
162    auto pipelineContext = container->GetPipelineContext();
163    if (!pipelineContext) {
164        LOGE("pipelineContext is null!");
165        return nullptr;
166    }
167    auto themeManager = pipelineContext->GetThemeManager();
168    if (!themeManager) {
169        LOGE("themeManager is null!");
170        return nullptr;
171    }
172    if (bundleName.has_value() && moduleName.has_value()) {
173        return themeManager->GetThemeConstants(bundleName.value_or(""), moduleName.value_or(""));
174    }
175    return themeManager->GetThemeConstants();
176}
177
178RefPtr<ResourceWrapper> CreateResourceWrapper(const ResourceInfo& info)
179{
180    auto bundleName = info.bundleName;
181    auto moduleName = info.moduleName;
182
183    RefPtr<ResourceAdapter> resourceAdapter = nullptr;
184    RefPtr<ThemeConstants> themeConstants = nullptr;
185    if (SystemProperties::GetResourceDecoupling()) {
186        if (bundleName.has_value() && moduleName.has_value()) {
187            auto resourceObject = AceType::MakeRefPtr<ResourceObject>(bundleName.value_or(""), moduleName.value_or(""));
188            resourceAdapter = ResourceManager::GetInstance().GetOrCreateResourceAdapter(resourceObject);
189        } else {
190            resourceAdapter = ResourceManager::GetInstance().GetResourceAdapter();
191        }
192        if (!resourceAdapter) {
193            return nullptr;
194        }
195    } else {
196        themeConstants = GetThemeConstants(info.bundleName, info.moduleName);
197        if (!themeConstants) {
198            return nullptr;
199        }
200    }
201    auto resourceWrapper = AceType::MakeRefPtr<ResourceWrapper>(themeConstants, resourceAdapter);
202    return resourceWrapper;
203}
204
205napi_value CreateNapiString(napi_env env, const std::string& rawStr)
206{
207    napi_value retVal = nullptr;
208    napi_create_string_utf8(env, rawStr.c_str(), rawStr.length(), &retVal);
209    return retVal;
210}
211
212bool ConvertResourceType(const std::string& typeName, ResourceType& resType)
213{
214    static const std::unordered_map<std::string, ResourceType> resTypeMap {
215        { "color", ResourceType::COLOR },
216        { "media", ResourceType::MEDIA },
217        { "float", ResourceType::FLOAT },
218        { "string", ResourceType::STRING },
219        { "plural", ResourceType::PLURAL },
220        { "pattern", ResourceType::PATTERN },
221        { "boolean", ResourceType::BOOLEAN },
222        { "integer", ResourceType::INTEGER },
223        { "strarray", ResourceType::STRARRAY },
224        { "intarray", ResourceType::INTARRAY },
225    };
226    auto it = resTypeMap.find(typeName);
227    if (it == resTypeMap.end()) {
228        return false;
229    }
230    resType = it->second;
231    return true;
232}
233
234bool ParseDollarResource(
235    napi_env env, napi_value value, ResourceType& resType, std::string& resName, std::string& moduleName)
236{
237    napi_valuetype valueType = napi_undefined;
238    napi_typeof(env, value, &valueType);
239    if (valueType != napi_string) {
240        return false;
241    }
242    std::string resPath;
243    if (!GetNapiString(env, value, resPath, valueType)) {
244        return false;
245    }
246    std::vector<std::string> tokens;
247    StringUtils::StringSplitter(resPath, '.', tokens);
248    // $r format like app.xxx.xxx, has 3 paragraph
249    if (static_cast<int32_t>(tokens.size()) != 3) {
250        return false;
251    }
252    std::string maybeModuleName = tokens[0];
253    // [*] or app/hsp at least has 3 chars
254    if (maybeModuleName.size() < 3) {
255        return false;
256    }
257    char begin = *maybeModuleName.begin();
258    char end = maybeModuleName.at(maybeModuleName.size() - 1);
259    bool headCheckPass = false;
260    if (begin == '[' && end == ']') {
261        // moduleName not include 2 brackets
262        moduleName = maybeModuleName.substr(1, maybeModuleName.size() - 2);
263        headCheckPass = true;
264    }
265    if (std::find(RESOURCE_HEADS.begin(), RESOURCE_HEADS.end(), tokens[0]) == RESOURCE_HEADS.end() && !headCheckPass) {
266        return false;
267    }
268    if (!ConvertResourceType(tokens[1], resType)) {
269        return false;
270    }
271    resName = resPath;
272    return true;
273}
274
275void PreFixEmptyBundleName(napi_env env, napi_value value)
276{
277    napi_value bundleNameNApi = nullptr;
278    if (napi_get_named_property(env, value, BUNDLE_NAME, &bundleNameNApi) != napi_ok) {
279        return;
280    }
281    std::string bundleName;
282    NapiStringToString(env, bundleNameNApi, bundleName);
283    if (bundleName.empty()) {
284        auto container = Container::CurrentSafely();
285        CHECK_NULL_VOID(container);
286        bundleName = container->GetBundleName();
287        bundleNameNApi = CreateNapiString(env, bundleName);
288        napi_set_named_property(env, value, BUNDLE_NAME, bundleNameNApi);
289    }
290}
291
292ResourceStruct CheckResourceStruct(napi_env env, napi_value value)
293{
294    napi_value idNApi = nullptr;
295    napi_valuetype valueType = napi_undefined;
296    napi_typeof(env, value, &valueType);
297    if (valueType != napi_object) {
298        return ResourceStruct::CONSTANT;
299    }
300    if (napi_get_named_property(env, value, "id", &idNApi) != napi_ok) {
301        return ResourceStruct::CONSTANT;
302    }
303    napi_typeof(env, idNApi, &valueType);
304    if (valueType == napi_string) {
305        return ResourceStruct::DYNAMIC_V1;
306    }
307    if (valueType == napi_number) {
308        int32_t id = 0;
309        napi_get_value_int32(env, idNApi, &id);
310        if (id == UNKNOWN_RESOURCE_ID) {
311            return ResourceStruct::DYNAMIC_V2;
312        }
313    }
314    return ResourceStruct::CONSTANT;
315}
316
317void CompleteResourceParam(napi_env env, napi_value value)
318{
319    PreFixEmptyBundleName(env, value);
320    ResourceStruct resourceStruct = CheckResourceStruct(env, value);
321    switch (resourceStruct) {
322        case ResourceStruct::CONSTANT:
323            return;
324        case ResourceStruct::DYNAMIC_V1:
325            CompleteResourceParamV1(env, value);
326            return;
327        case ResourceStruct::DYNAMIC_V2:
328            CompleteResourceParamV2(env, value);
329            return;
330        default:
331            return;
332    }
333}
334
335void CompleteResourceParamV1(napi_env env, napi_value value)
336{
337    napi_value idNApi = nullptr;
338    napi_valuetype valueType = napi_undefined;
339    napi_typeof(env, value, &valueType);
340    if (valueType != napi_object) {
341        return;
342    }
343    if (napi_get_named_property(env, value, "id", &idNApi) != napi_ok) {
344        return;
345    }
346    std::string resName;
347    std::string moduleName;
348    ResourceType resType;
349    if (!ParseDollarResource(env, idNApi, resType, resName, moduleName)) {
350        return;
351    }
352    bool hasProperty = false;
353    napi_value typeIdNApi = nullptr;
354    napi_value resourceIdNApi = nullptr;
355    napi_value typeKeyNApi = CreateNapiString(env, "type");
356    napi_value defaultNameNApi = CreateNapiString(env, "");
357    napi_value bundleNameKeyNApi = CreateNapiString(env, "bundleName");
358    napi_value moduleNameKeyNApi = CreateNapiString(env, "moduleName");
359    napi_create_int32(env, UNKNOWN_RESOURCE_ID, &resourceIdNApi);
360    napi_create_int32(env, static_cast<uint32_t>(resType), &typeIdNApi);
361    ModifyResourceParam(env, value, resType, resName);
362    napi_set_property(env, value, typeKeyNApi, typeIdNApi);
363    napi_set_property(env, value, CreateNapiString(env, "id"), resourceIdNApi);
364    napi_has_property(env, value, bundleNameKeyNApi, &hasProperty);
365    if (!hasProperty) {
366        napi_set_property(env, value, bundleNameKeyNApi, defaultNameNApi);
367    }
368    napi_has_property(env, value, moduleNameKeyNApi, &hasProperty);
369    if (!hasProperty) {
370        napi_set_property(env, value, moduleNameKeyNApi, defaultNameNApi);
371    }
372}
373
374void CompleteResourceParamV2(napi_env env, napi_value value)
375{
376    napi_value paramsNApi = nullptr;
377    if (napi_get_named_property(env, value, "params", &paramsNApi) != napi_ok) {
378        return;
379    }
380    bool isArray = false;
381    napi_is_array(env, paramsNApi, &isArray);
382    if (!isArray) {
383        return;
384    }
385    uint32_t paramCount = 0;
386    napi_get_array_length(env, paramsNApi, &paramCount);
387    if (paramCount <= 0) {
388        return;
389    }
390    napi_value resNameNApi = nullptr;
391    napi_get_element(env, paramsNApi, 0, &resNameNApi);
392    std::string resName;
393    std::string moduleName;
394    ResourceType resType;
395    if (!ParseDollarResource(env, resNameNApi, resType, resName, moduleName)) {
396        return;
397    }
398    napi_value typeIdNApi = nullptr;
399    napi_value typeKeyNApi = CreateNapiString(env, "type");
400    napi_create_int32(env, static_cast<uint32_t>(resType), &typeIdNApi);
401    napi_set_property(env, value, typeKeyNApi, typeIdNApi);
402    if (!moduleName.empty()) {
403        napi_value moduleNameNApi = CreateNapiString(env, moduleName);
404        napi_value moduleNameKeyNApi = CreateNapiString(env, "moduleName");
405        napi_set_property(env, value, moduleNameKeyNApi, moduleNameNApi);
406    }
407}
408
409void ModifyResourceParam(napi_env env, napi_value value, const ResourceType& resType, const std::string& resName)
410{
411    // raw input : {"id":"app.xxx.xxx","params":[],"moduleName":"xxx","bundleName":"xxx"}
412    // modified output : {"id":-1, "params":["app.xxx.xxx"],"type":xxxx,"moduleName":"xxx","bundleName":"xxx"}
413    napi_value paramsNApi = nullptr;
414    napi_get_named_property(env, value, "params", &paramsNApi);
415    bool isArray = false;
416    if (napi_is_array(env, paramsNApi, &isArray) != napi_ok) {
417        return;
418    }
419    if (!isArray) {
420        return;
421    }
422    uint32_t paramCount = 0;
423    bool hasProperty = false;
424    napi_get_array_length(env, paramsNApi, &paramCount);
425    napi_value typeKeyNApi = CreateNapiString(env, "type");
426    napi_value resNameNApi = CreateNapiString(env, resName);
427    if (resType == ResourceType::PLURAL || resType == ResourceType::STRING) {
428        std::vector<napi_value> tmpParams;
429        for (uint32_t i = 0; i < paramCount; i++) {
430            napi_value param = nullptr;
431            napi_get_element(env, paramsNApi, i, &param);
432            tmpParams.insert(tmpParams.end(), param);
433        }
434        napi_set_element(env, paramsNApi, 0, resNameNApi);
435        uint32_t paramIndex = 1;
436        napi_has_property(env, value, typeKeyNApi, &hasProperty);
437        if (hasProperty) {
438            napi_value firstParam = nullptr;
439            napi_get_property(env, value, typeKeyNApi, &firstParam);
440            napi_set_element(env, paramsNApi, paramIndex, firstParam);
441            paramIndex++;
442        }
443        for (auto tmpParam : tmpParams) {
444            napi_set_element(env, paramsNApi, paramIndex, tmpParam);
445            paramIndex++;
446        }
447    } else {
448        napi_set_element(env, paramsNApi, 0, resNameNApi);
449    }
450}
451
452void ParseCurveInfo(const std::string& curveString, std::string& curveTypeString, std::vector<float>& curveValue)
453{
454    if (curveString.back() != ')') {
455        return;
456    }
457    std::string::size_type leftEmbracePosition = curveString.find_last_of('(');
458    if (leftEmbracePosition == std::string::npos) {
459        return;
460    }
461    curveTypeString = curveString.substr(0, leftEmbracePosition);
462    auto params = curveString.substr(leftEmbracePosition + 1, curveString.length() - leftEmbracePosition - 2);
463    if (curveTypeString.empty() || params.empty()) {
464        return;
465    }
466    std::vector<std::string> paramsVector;
467    StringUtils::StringSplitter(params, ',', paramsVector);
468    for (auto& param : paramsVector) {
469        Framework::RemoveHeadTailSpace(param);
470        if (param == "true" || param == "start") {
471            param = "1.000000";
472        }
473        if (param == "false" || param == "end") {
474            param = "0.000000";
475        }
476        errno = 0;
477        char* end = nullptr;
478        float value = strtof(param.c_str(), &end);
479        if (end == param.c_str() || errno == ERANGE) {
480            LOGW("%{public}s can not be converted to float or is out of range.", param.c_str());
481        }
482        curveValue.emplace_back(value);
483    }
484}
485
486napi_value ParseCurve(napi_env env, napi_value value, std::string& curveTypeString, std::vector<float>& curveValue)
487{
488    CHECK_NULL_RETURN(value, nullptr);
489    napi_valuetype valueType = napi_undefined;
490    napi_typeof(env, value, &valueType);
491    NAPI_ASSERT(env, valueType == napi_object || valueType == napi_string, "The type of curve is incorrect");
492    if (valueType == napi_object) {
493        napi_value curveObjectNApi = nullptr;
494        napi_get_named_property(env, value, "__curveString", &curveObjectNApi);
495        value = curveObjectNApi;
496    }
497
498    size_t paramLen = 0;
499    napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &paramLen);
500    NAPI_ASSERT(env, paramLen > 0 && paramLen < NAPI_BUF_LENGTH && status == napi_ok, "paramLen error");
501    char params[NAPI_BUF_LENGTH] = { 0 };
502    status = napi_get_value_string_utf8(env, value, params, paramLen + 1, &paramLen);
503    NAPI_ASSERT(env, status == napi_ok, "Parse curve failed");
504
505    RefPtr<Curve> curve;
506    const std::string domAnimationDefaultCurveString = "ease-in-out";
507    if (params[0] == '\0') {
508        curve = Framework::CreateCurve(domAnimationDefaultCurveString);
509    } else {
510        curve = Framework::CreateCurve(params);
511    }
512    std::string curveString = curve->ToString();
513    ParseCurveInfo(curveString, curveTypeString, curveValue);
514    return nullptr;
515}
516
517napi_valuetype GetValueType(napi_env env, napi_value value)
518{
519    if (value == nullptr) {
520        return napi_undefined;
521    }
522
523    napi_valuetype valueType = napi_undefined;
524    NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
525    return valueType;
526}
527
528std::optional<std::string> GetStringFromValueUtf8(napi_env env, napi_value value)
529{
530    static constexpr size_t maxLength = 2048;
531    if (GetValueType(env, value) != napi_string) {
532        return std::nullopt;
533    }
534
535    size_t paramLen = 0;
536    napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &paramLen);
537    if (paramLen == 0 || paramLen > maxLength || status != napi_ok) {
538        return std::nullopt;
539    }
540    char params[maxLength] = { 0 };
541    status = napi_get_value_string_utf8(env, value, params, paramLen + 1, &paramLen);
542    if (status != napi_ok) {
543        return std::nullopt;
544    }
545    return params;
546}
547
548bool GetIntProperty(napi_env env, napi_value value, const std::string& key, int32_t& result)
549{
550    CHECK_NULL_RETURN(value, false);
551    napi_valuetype valueType = napi_undefined;
552    napi_value propertyNApi = nullptr;
553    napi_get_named_property(env, value, key.c_str(), &propertyNApi);
554    if (valueType != napi_number) {
555        LOGE("The type of property is incorrect");
556        return false;
557    }
558    int32_t property = 0;
559    napi_status status = napi_get_value_int32(env, propertyNApi, &property);
560    if (status != napi_ok) {
561        LOGE("Get property failed");
562        return false;
563    }
564    return true;
565}
566
567static uint32_t CompleteColorAlphaIfIncomplete(uint32_t origin)
568{
569    constexpr uint32_t colorAlphaOffset = 24;
570    constexpr uint32_t colorAlphaDefaultValue = 0xFF000000;
571    uint32_t result = origin;
572    if ((origin >> colorAlphaOffset) == 0) {
573        result = origin | colorAlphaDefaultValue;
574    }
575    return result;
576}
577
578bool ParseColorFromResourceObject(napi_env env, napi_value value, Color& colorResult)
579{
580    ResourceInfo resourceInfo;
581    if (!ParseResourceParam(env, value, resourceInfo)) {
582        LOGE("Parse color from resource failed");
583        return false;
584    }
585    auto themeConstants = GetThemeConstants(resourceInfo.bundleName, resourceInfo.moduleName);
586    if (themeConstants == nullptr) {
587        LOGE("themeConstants is nullptr");
588        return false;
589    }
590    if (resourceInfo.type == static_cast<int32_t>(ResourceType::STRING)) {
591        auto colorString = themeConstants->GetString(resourceInfo.type);
592        return Color::ParseColorString(colorString, colorResult);
593    }
594    if (resourceInfo.type == static_cast<int32_t>(ResourceType::INTEGER)) {
595        auto colorInt = themeConstants->GetInt(resourceInfo.type);
596        colorResult = Color(CompleteColorAlphaIfIncomplete(colorInt));
597        return true;
598    }
599    colorResult = themeConstants->GetColor(resourceInfo.resId);
600    return true;
601}
602
603bool ParseColor(napi_env env, napi_value value, Color& result)
604{
605    napi_valuetype valueType = GetValueType(env, value);
606    if (valueType != napi_number && valueType != napi_string && valueType != napi_object) {
607        return false;
608    }
609    if (valueType == napi_number) {
610        int32_t colorId = 0;
611        napi_get_value_int32(env, value, &colorId);
612        result = Color(CompleteColorAlphaIfIncomplete(static_cast<uint32_t>(colorId)));
613        return true;
614    }
615    if (valueType == napi_string) {
616        std::optional<std::string> colorString = GetStringFromValueUtf8(env, value);
617        if (!colorString.has_value()) {
618            LOGE("Parse color from string failed");
619        }
620        return Color::ParseColorString(colorString.value(), result);
621    }
622
623    return ParseColorFromResourceObject(env, value, result);
624}
625
626bool ParseResourceParam(napi_env env, napi_value value, ResourceInfo& info)
627{
628    CompleteResourceParam(env, value);
629    napi_value idNApi = nullptr;
630    napi_value typeNApi = nullptr;
631    napi_value paramsNApi = nullptr;
632    napi_value bundleNameNApi = nullptr;
633    napi_value moduleNameNApi = nullptr;
634    napi_valuetype valueType = napi_undefined;
635    napi_typeof(env, value, &valueType);
636    if (valueType == napi_object) {
637        napi_get_named_property(env, value, "id", &idNApi);
638        napi_get_named_property(env, value, "type", &typeNApi);
639        napi_get_named_property(env, value, "params", &paramsNApi);
640        napi_get_named_property(env, value, "bundleName", &bundleNameNApi);
641        napi_get_named_property(env, value, "moduleName", &moduleNameNApi);
642    } else {
643        return false;
644    }
645
646    napi_typeof(env, idNApi, &valueType);
647    if (valueType == napi_number) {
648        napi_get_value_int32(env, idNApi, &info.resId);
649    }
650
651    napi_typeof(env, typeNApi, &valueType);
652    if (valueType == napi_number) {
653        napi_get_value_int32(env, typeNApi, &info.type);
654    }
655
656    bool isArray = false;
657    if (napi_is_array(env, paramsNApi, &isArray) != napi_ok) {
658        return false;
659    }
660
661    if (!isArray) {
662        return false;
663    }
664
665    uint32_t arrayLength = 0;
666    napi_get_array_length(env, paramsNApi, &arrayLength);
667
668    for (uint32_t i = 0; i < arrayLength; i++) {
669        size_t ret = 0;
670        napi_value indexValue = nullptr;
671        napi_get_element(env, paramsNApi, i, &indexValue);
672        napi_typeof(env, indexValue, &valueType);
673        if (valueType == napi_string) {
674            size_t strLen = GetParamLen(env, indexValue) + 1;
675            std::unique_ptr<char[]> indexStr = std::make_unique<char[]>(strLen);
676            napi_get_value_string_utf8(env, indexValue, indexStr.get(), strLen, &ret);
677            info.params.emplace_back(indexStr.get());
678        } else if (valueType == napi_number) {
679            int32_t num;
680            napi_get_value_int32(env, indexValue, &num);
681            info.params.emplace_back(std::to_string(num));
682        }
683    }
684
685    napi_typeof(env, bundleNameNApi, &valueType);
686    if (valueType == napi_string) {
687        size_t ret = 0;
688        size_t strLen = GetParamLen(env, bundleNameNApi) + 1;
689        std::unique_ptr<char[]> bundleNameStr = std::make_unique<char[]>(strLen);
690        napi_get_value_string_utf8(env, bundleNameNApi, bundleNameStr.get(), strLen, &ret);
691        info.bundleName = bundleNameStr.get();
692    }
693
694    napi_typeof(env, moduleNameNApi, &valueType);
695    if (valueType == napi_string) {
696        size_t ret = 0;
697        size_t strLen = GetParamLen(env, moduleNameNApi) + 1;
698        std::unique_ptr<char[]> moduleNameStr = std::make_unique<char[]>(strLen);
699        napi_get_value_string_utf8(env, moduleNameNApi, moduleNameStr.get(), strLen, &ret);
700        info.moduleName = moduleNameStr.get();
701    }
702
703    return true;
704}
705
706std::string DimensionToString(Dimension dimension)
707{
708    static const int32_t unitsNum = 6;
709    static const int32_t percentIndex = 3;
710    static const int32_t percentUnit = 100;
711    static std::array<std::string, unitsNum> units = { "px", "vp", "fp", "%", "lpx", "auto" };
712    auto unit = dimension.Unit();
713    auto value = dimension.Value();
714    if (unit == DimensionUnit::NONE) {
715        return StringUtils::DoubleToString(value).append("none");
716    }
717    if (units[static_cast<int>(unit)] == units[percentIndex]) {
718        return StringUtils::DoubleToString(value * percentUnit).append(units[static_cast<int>(unit)]);
719    }
720    return StringUtils::DoubleToString(value).append(units[static_cast<int>(unit)]);
721}
722
723bool ParseString(const ResourceInfo& info, std::string& result)
724{
725    auto resourceWrapper = CreateResourceWrapper(info);
726    if (info.type == static_cast<int>(ResourceType::PLURAL)) {
727        std::string pluralResults;
728        if (info.resId == UNKNOWN_RESOURCE_ID) {
729            auto count = StringUtils::StringToInt(info.params[1]);
730            pluralResults = resourceWrapper->GetPluralStringByName(info.params[0], count);
731            ReplaceHolder(pluralResults, info.params, 2); // plural holder in index 2
732        } else {
733            auto count = StringUtils::StringToInt(info.params[0]);
734            pluralResults = resourceWrapper->GetPluralString(info.resId, count);
735            ReplaceHolder(pluralResults, info.params, 1);
736        }
737        result = pluralResults;
738        return true;
739    }
740    if (info.type == static_cast<int>(ResourceType::RAWFILE)) {
741        auto fileName = info.params[0];
742        result = resourceWrapper->GetRawfile(fileName);
743        return true;
744    }
745    if (info.type == static_cast<int>(ResourceType::FLOAT)) {
746        if (info.resId == UNKNOWN_RESOURCE_ID) {
747            result = DimensionToString(resourceWrapper->GetDimensionByName(info.params[0]));
748        } else {
749            result = DimensionToString(resourceWrapper->GetDimension(info.resId));
750        }
751        return true;
752    }
753    if (info.type == static_cast<int>(ResourceType::STRING)) {
754        std::string originStr;
755        if (info.resId == UNKNOWN_RESOURCE_ID) {
756            originStr = resourceWrapper->GetStringByName(info.params[0]);
757            ReplaceHolder(originStr, info.params, 1);
758        } else {
759            originStr = resourceWrapper->GetString(info.resId);
760            ReplaceHolder(originStr, info.params, 0);
761        }
762        result = originStr;
763        return true;
764    }
765    if (info.type == static_cast<int>(ResourceType::COLOR)) {
766        result = resourceWrapper->GetColor(info.resId).ColorToString();
767        return true;
768    }
769    if (info.type == static_cast<int>(ResourceType::INTEGER)) {
770        result = std::to_string(resourceWrapper->GetInt(info.resId));
771        return true;
772    }
773    return true;
774}
775
776std::string ErrorToMessage(int32_t code)
777{
778    auto iter = ERROR_CODE_TO_MSG.find(code);
779    return (iter != ERROR_CODE_TO_MSG.end()) ? iter->second : "";
780}
781
782bool GetSingleParam(napi_env env, napi_callback_info info, napi_value* argv, napi_valuetype& valueType)
783{
784    size_t argc = 1;
785    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
786    if (argc != 1) {
787        return false;
788    }
789    napi_typeof(env, argv[0], &valueType);
790    return true;
791}
792
793// (Color | number | string | undifened)
794std::optional<Color> GetOptionalColor(napi_env env, napi_value argv, napi_valuetype& valueType)
795{
796    if (valueType == napi_number) {
797        uint32_t num;
798        uint32_t alpha = 0xff000000;
799        napi_get_value_uint32(env, argv, &num);
800        if ((num & alpha) == 0) {
801            num |= alpha;
802        }
803        return Color(num);
804    } else if (valueType == napi_string) {
805        std::string str;
806        bool result = GetNapiString(env, argv, str, valueType);
807        Color color;
808        if (!result || !Color::ParseColorString(str, color)) {
809            return std::nullopt;
810        }
811        return color;
812    } else {
813        return std::nullopt;
814    }
815}
816
817bool ParseIntegerToString(const ResourceInfo& info, std::string& result)
818{
819    auto resourceWrapper = CreateResourceWrapper(info);
820    if (info.type == static_cast<int>(ResourceType::INTEGER)) {
821        if (info.resId == UNKNOWN_RESOURCE_ID) {
822            result = std::to_string(resourceWrapper->GetIntByName(info.params[0]));
823        } else {
824            result = std::to_string(resourceWrapper->GetInt(info.resId));
825        }
826        return true;
827    }
828    return true;
829}
830
831bool HasProperty(napi_env env, napi_value value, const std::string& targetStr)
832{
833    bool hasProperty = false;
834    napi_has_named_property(env, value, targetStr.c_str(), &hasProperty);
835    return hasProperty;
836}
837
838napi_value GetReturnObject(napi_env env, std::string callbackString)
839{
840    napi_value result = nullptr;
841    napi_value returnObj = nullptr;
842    napi_create_object(env, &returnObj);
843    napi_create_string_utf8(env, callbackString.c_str(), NAPI_AUTO_LENGTH, &result);
844    napi_set_named_property(env, returnObj, "errMsg", result);
845    return returnObj;
846}
847
848bool ParseNapiDimension(napi_env env, CalcDimension& result, napi_value napiValue, DimensionUnit defaultUnit)
849{
850    napi_valuetype valueType = napi_undefined;
851    napi_typeof(env, napiValue, &valueType);
852    if (valueType == napi_number) {
853        double value = 0;
854        napi_get_value_double(env, napiValue, &value);
855        result.SetUnit(defaultUnit);
856        result.SetValue(value);
857        return true;
858    } else if (valueType == napi_string) {
859        std::string valueString;
860        if (!GetNapiString(env, napiValue, valueString, valueType)) {
861            return false;
862        }
863        result = StringUtils::StringToCalcDimension(valueString, false, defaultUnit);
864        return true;
865    } else if (valueType == napi_object) {
866        ResourceInfo recv;
867        std::string parameterStr;
868        if (!ParseResourceParam(env, napiValue, recv)) {
869            return false;
870        }
871        if (!ParseString(recv, parameterStr)) {
872            return false;
873        }
874        result = StringUtils::StringToDimensionWithUnit(parameterStr, defaultUnit);
875        return true;
876    }
877    return false;
878}
879
880bool ParseNapiDimensionNG(
881    napi_env env, CalcDimension& result, napi_value napiValue, DimensionUnit defaultUnit, bool isSupportPercent)
882{
883    napi_valuetype valueType = napi_undefined;
884    napi_typeof(env, napiValue, &valueType);
885    if (valueType == napi_number) {
886        double value = 0;
887        napi_get_value_double(env, napiValue, &value);
888
889        result.SetUnit(defaultUnit);
890        result.SetValue(value);
891        return true;
892    } else if (valueType == napi_string) {
893        std::string valueString;
894        if (!GetNapiString(env, napiValue, valueString, valueType)) {
895            return false;
896        }
897        if (valueString.back() == '%' && !isSupportPercent) {
898            return false;
899        }
900        return StringUtils::StringToCalcDimensionNG(valueString, result, false, defaultUnit);
901    } else if (valueType == napi_object) {
902        ResourceInfo recv;
903        std::string parameterStr;
904        if (!ParseResourceParam(env, napiValue, recv)) {
905            return false;
906        }
907        if (!ParseString(recv, parameterStr)) {
908            return false;
909        }
910        if (!ParseIntegerToString(recv, parameterStr)) {
911            return false;
912        }
913        result = StringUtils::StringToDimensionWithUnit(parameterStr, defaultUnit);
914        return true;
915    }
916    return false;
917}
918
919bool ParseNapiColor(napi_env env, napi_value value, Color& result)
920{
921    napi_valuetype valueType = GetValueType(env, value);
922    if (valueType != napi_number && valueType != napi_string && valueType != napi_object) {
923        return false;
924    }
925    if (valueType == napi_number) {
926        int32_t colorId = 0;
927        napi_get_value_int32(env, value, &colorId);
928        constexpr uint32_t colorAlphaOffset = 24;
929        constexpr uint32_t colorAlphaDefaultValue = 0xFF000000;
930        auto origin = static_cast<uint32_t>(colorId);
931        uint32_t alphaResult = origin;
932        if ((origin >> colorAlphaOffset) == 0) {
933            alphaResult = origin | colorAlphaDefaultValue;
934        }
935        result = Color(alphaResult);
936        return true;
937    }
938    if (valueType == napi_string) {
939        std::optional<std::string> colorString = GetStringFromValueUtf8(env, value);
940        if (!colorString.has_value()) {
941            LOGE("Parse color from string failed");
942            return false;
943        }
944        return Color::ParseColorString(colorString.value(), result);
945    }
946
947    return ParseColorFromResourceObject(env, value, result);
948}
949
950bool ParseStyle(napi_env env, napi_value value, std::optional<BorderStyle>& style)
951{
952    napi_valuetype valueType = GetValueType(env, value);
953    if (valueType != napi_number) {
954        return false;
955    }
956    int32_t num;
957    napi_get_value_int32(env, value, &num);
958    style = static_cast<BorderStyle>(num);
959    if (style < BorderStyle::SOLID || style > BorderStyle::NONE) {
960        return false;
961    }
962    return true;
963}
964
965bool ParseShadowColorStrategy(napi_env env, napi_value value, ShadowColorStrategy& strategy)
966{
967    napi_valuetype valueType = GetValueType(env, value);
968    if (valueType == napi_string) {
969        std::optional<std::string> colorStr = GetStringFromValueUtf8(env, value);
970        if (colorStr.has_value()) {
971            if (colorStr->compare("average") == 0) {
972                strategy = ShadowColorStrategy::AVERAGE;
973                return true;
974            } else if (colorStr->compare("primary") == 0) {
975                strategy = ShadowColorStrategy::PRIMARY;
976                return true;
977            }
978        }
979    }
980    return false;
981}
982} // namespace OHOS::Ace::Napi
983