13920e296Sopenharmony_ci/*
23920e296Sopenharmony_ci * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
33920e296Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
43920e296Sopenharmony_ci * you may not use this file except in compliance with the License.
53920e296Sopenharmony_ci * You may obtain a copy of the License at
63920e296Sopenharmony_ci *
73920e296Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
83920e296Sopenharmony_ci *
93920e296Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
103920e296Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
113920e296Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123920e296Sopenharmony_ci * See the License for the specific language governing permissions and
133920e296Sopenharmony_ci * limitations under the License.
143920e296Sopenharmony_ci */
153920e296Sopenharmony_ci
163920e296Sopenharmony_ci#include "json_compiler.h"
173920e296Sopenharmony_ci#include <iostream>
183920e296Sopenharmony_ci#include <limits>
193920e296Sopenharmony_ci#include <regex>
203920e296Sopenharmony_ci#include "restool_errors.h"
213920e296Sopenharmony_ci#include "translatable_parser.h"
223920e296Sopenharmony_ci
233920e296Sopenharmony_cinamespace OHOS {
243920e296Sopenharmony_cinamespace Global {
253920e296Sopenharmony_cinamespace Restool {
263920e296Sopenharmony_ciusing namespace std;
273920e296Sopenharmony_ciconst string TAG_NAME = "name";
283920e296Sopenharmony_ciconst string TAG_VALUE = "value";
293920e296Sopenharmony_ciconst string TAG_PARENT = "parent";
303920e296Sopenharmony_ciconst string TAG_QUANTITY = "quantity";
313920e296Sopenharmony_ciconst vector<string> QUANTITY_ATTRS = { "zero", "one", "two", "few", "many", "other" };
323920e296Sopenharmony_ciconst vector<string> TRANSLATION_TYPE = { "string", "strarray", "plural" };
333920e296Sopenharmony_ci
343920e296Sopenharmony_ciJsonCompiler::JsonCompiler(ResType type, const string &output)
353920e296Sopenharmony_ci    : IResourceCompiler(type, output), isBaseString_(false), root_(nullptr)
363920e296Sopenharmony_ci{
373920e296Sopenharmony_ci    InitParser();
383920e296Sopenharmony_ci}
393920e296Sopenharmony_ci
403920e296Sopenharmony_ciJsonCompiler::~JsonCompiler()
413920e296Sopenharmony_ci{
423920e296Sopenharmony_ci    if (root_) {
433920e296Sopenharmony_ci        cJSON_Delete(root_);
443920e296Sopenharmony_ci    }
453920e296Sopenharmony_ci}
463920e296Sopenharmony_ci
473920e296Sopenharmony_ciuint32_t JsonCompiler::CompileSingleFile(const FileInfo &fileInfo)
483920e296Sopenharmony_ci{
493920e296Sopenharmony_ci    if (fileInfo.limitKey == "base" &&
503920e296Sopenharmony_ci        fileInfo.fileCluster == "element" &&
513920e296Sopenharmony_ci        fileInfo.filename == ID_DEFINED_FILE) {
523920e296Sopenharmony_ci        return RESTOOL_SUCCESS;
533920e296Sopenharmony_ci    }
543920e296Sopenharmony_ci
553920e296Sopenharmony_ci    if (!ResourceUtil::OpenJsonFile(fileInfo.filePath, &root_)) {
563920e296Sopenharmony_ci        return RESTOOL_ERROR;
573920e296Sopenharmony_ci    }
583920e296Sopenharmony_ci    if (!root_ || !cJSON_IsObject(root_)) {
593920e296Sopenharmony_ci        cerr << "Error: JSON file parsing failed, please check the JSON file.";
603920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
613920e296Sopenharmony_ci        return RESTOOL_ERROR;
623920e296Sopenharmony_ci    }
633920e296Sopenharmony_ci    cJSON *item = root_->child;
643920e296Sopenharmony_ci    if (cJSON_GetArraySize(root_) != 1) {
653920e296Sopenharmony_ci        cerr << "Error: node of a JSON file can only have one member, please check the JSON file.";
663920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
673920e296Sopenharmony_ci        return RESTOOL_ERROR;
683920e296Sopenharmony_ci    }
693920e296Sopenharmony_ci
703920e296Sopenharmony_ci    string tag = item->string;
713920e296Sopenharmony_ci    auto ret = g_contentClusterMap.find(tag);
723920e296Sopenharmony_ci    if (ret == g_contentClusterMap.end()) {
733920e296Sopenharmony_ci        cerr << "Error: invalid tag name '" << tag << "', please check the JSON file.";
743920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
753920e296Sopenharmony_ci        return RESTOOL_ERROR;
763920e296Sopenharmony_ci    }
773920e296Sopenharmony_ci    isBaseString_ = (fileInfo.limitKey == "base" &&
783920e296Sopenharmony_ci        find(TRANSLATION_TYPE.begin(), TRANSLATION_TYPE.end(), tag) != TRANSLATION_TYPE.end());
793920e296Sopenharmony_ci    FileInfo copy = fileInfo;
803920e296Sopenharmony_ci    copy.fileType = ret->second;
813920e296Sopenharmony_ci    if (!ParseJsonArrayLevel(item, copy)) {
823920e296Sopenharmony_ci        return RESTOOL_ERROR;
833920e296Sopenharmony_ci    }
843920e296Sopenharmony_ci    return RESTOOL_SUCCESS;
853920e296Sopenharmony_ci}
863920e296Sopenharmony_ci
873920e296Sopenharmony_ci// below private
883920e296Sopenharmony_civoid JsonCompiler::InitParser()
893920e296Sopenharmony_ci{
903920e296Sopenharmony_ci    using namespace placeholders;
913920e296Sopenharmony_ci    handles_.emplace(ResType::STRING, bind(&JsonCompiler::HandleString, this, _1, _2));
923920e296Sopenharmony_ci    handles_.emplace(ResType::INTEGER, bind(&JsonCompiler::HandleInteger, this, _1, _2));
933920e296Sopenharmony_ci    handles_.emplace(ResType::BOOLEAN, bind(&JsonCompiler::HandleBoolean, this, _1, _2));
943920e296Sopenharmony_ci    handles_.emplace(ResType::COLOR, bind(&JsonCompiler::HandleColor, this, _1, _2));
953920e296Sopenharmony_ci    handles_.emplace(ResType::FLOAT, bind(&JsonCompiler::HandleFloat, this, _1, _2));
963920e296Sopenharmony_ci    handles_.emplace(ResType::STRARRAY, bind(&JsonCompiler::HandleStringArray, this, _1, _2));
973920e296Sopenharmony_ci    handles_.emplace(ResType::INTARRAY, bind(&JsonCompiler::HandleIntegerArray, this, _1, _2));
983920e296Sopenharmony_ci    handles_.emplace(ResType::THEME, bind(&JsonCompiler::HandleTheme, this, _1, _2));
993920e296Sopenharmony_ci    handles_.emplace(ResType::PATTERN, bind(&JsonCompiler::HandlePattern, this, _1, _2));
1003920e296Sopenharmony_ci    handles_.emplace(ResType::PLURAL, bind(&JsonCompiler::HandlePlural, this, _1, _2));
1013920e296Sopenharmony_ci    handles_.emplace(ResType::SYMBOL, bind(&JsonCompiler::HandleSymbol, this, _1, _2));
1023920e296Sopenharmony_ci}
1033920e296Sopenharmony_ci
1043920e296Sopenharmony_cibool JsonCompiler::ParseJsonArrayLevel(const cJSON *arrayNode, const FileInfo &fileInfo)
1053920e296Sopenharmony_ci{
1063920e296Sopenharmony_ci    if (!arrayNode || !cJSON_IsArray(arrayNode)) {
1073920e296Sopenharmony_ci        cerr << "Error: '" << ResourceUtil::ResTypeToString(fileInfo.fileType) << "' must be array.";
1083920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
1093920e296Sopenharmony_ci        return false;
1103920e296Sopenharmony_ci    }
1113920e296Sopenharmony_ci
1123920e296Sopenharmony_ci    if (cJSON_GetArraySize(arrayNode) == 0) {
1133920e296Sopenharmony_ci        cerr << "Error: '" << ResourceUtil::ResTypeToString(fileInfo.fileType) << "' empty.";
1143920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
1153920e296Sopenharmony_ci        return false;
1163920e296Sopenharmony_ci    }
1173920e296Sopenharmony_ci    int32_t index = -1;
1183920e296Sopenharmony_ci    for (cJSON *item = arrayNode->child; item; item = item->next) {
1193920e296Sopenharmony_ci        index++;
1203920e296Sopenharmony_ci        if (!item || !cJSON_IsObject(item)) {
1213920e296Sopenharmony_ci            cerr << "Error: the seq=" << index << " item must be object." << NEW_LINE_PATH << fileInfo.filePath << endl;
1223920e296Sopenharmony_ci            return false;
1233920e296Sopenharmony_ci        }
1243920e296Sopenharmony_ci        if (!ParseJsonObjectLevel(item, fileInfo)) {
1253920e296Sopenharmony_ci            return false;
1263920e296Sopenharmony_ci        }
1273920e296Sopenharmony_ci    }
1283920e296Sopenharmony_ci    return true;
1293920e296Sopenharmony_ci}
1303920e296Sopenharmony_ci
1313920e296Sopenharmony_cibool JsonCompiler::ParseJsonObjectLevel(cJSON *objectNode, const FileInfo &fileInfo)
1323920e296Sopenharmony_ci{
1333920e296Sopenharmony_ci    cJSON *nameNode = cJSON_GetObjectItem(objectNode, TAG_NAME.c_str());
1343920e296Sopenharmony_ci    if (!nameNode) {
1353920e296Sopenharmony_ci        cerr << "Error: name empty." << NEW_LINE_PATH << fileInfo.filePath << endl;
1363920e296Sopenharmony_ci        return false;
1373920e296Sopenharmony_ci    }
1383920e296Sopenharmony_ci
1393920e296Sopenharmony_ci    if (!cJSON_IsString(nameNode)) {
1403920e296Sopenharmony_ci        cerr << "Error: name must string." << NEW_LINE_PATH << fileInfo.filePath << endl;
1413920e296Sopenharmony_ci        return false;
1423920e296Sopenharmony_ci    }
1433920e296Sopenharmony_ci
1443920e296Sopenharmony_ci    if (isBaseString_ && !TranslatableParse::ParseTranslatable(objectNode, fileInfo, nameNode->valuestring)) {
1453920e296Sopenharmony_ci        return false;
1463920e296Sopenharmony_ci    }
1473920e296Sopenharmony_ci    ResourceItem resourceItem(nameNode->valuestring, fileInfo.keyParams, fileInfo.fileType);
1483920e296Sopenharmony_ci    resourceItem.SetFilePath(fileInfo.filePath);
1493920e296Sopenharmony_ci    resourceItem.SetLimitKey(fileInfo.limitKey);
1503920e296Sopenharmony_ci    auto ret = handles_.find(fileInfo.fileType);
1513920e296Sopenharmony_ci    if (ret == handles_.end()) {
1523920e296Sopenharmony_ci        cerr << "Error: json parser don't support " << ResourceUtil::ResTypeToString(fileInfo.fileType) << endl;
1533920e296Sopenharmony_ci        return false;
1543920e296Sopenharmony_ci    }
1553920e296Sopenharmony_ci
1563920e296Sopenharmony_ci    if (!ret->second(objectNode, resourceItem)) {
1573920e296Sopenharmony_ci        return false;
1583920e296Sopenharmony_ci    }
1593920e296Sopenharmony_ci
1603920e296Sopenharmony_ci    return MergeResourceItem(resourceItem);
1613920e296Sopenharmony_ci}
1623920e296Sopenharmony_ci
1633920e296Sopenharmony_cibool JsonCompiler::HandleString(const cJSON *objectNode, ResourceItem &resourceItem) const
1643920e296Sopenharmony_ci{
1653920e296Sopenharmony_ci    cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
1663920e296Sopenharmony_ci    if (!CheckJsonStringValue(valueNode, resourceItem)) {
1673920e296Sopenharmony_ci        return false;
1683920e296Sopenharmony_ci    }
1693920e296Sopenharmony_ci    return PushString(valueNode->valuestring, resourceItem);
1703920e296Sopenharmony_ci}
1713920e296Sopenharmony_ci
1723920e296Sopenharmony_cibool JsonCompiler::HandleInteger(const cJSON *objectNode, ResourceItem &resourceItem) const
1733920e296Sopenharmony_ci{
1743920e296Sopenharmony_ci    cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
1753920e296Sopenharmony_ci    if (!CheckJsonIntegerValue(valueNode, resourceItem)) {
1763920e296Sopenharmony_ci        return false;
1773920e296Sopenharmony_ci    }
1783920e296Sopenharmony_ci    if (cJSON_IsString(valueNode)) {
1793920e296Sopenharmony_ci        return PushString(valueNode->valuestring, resourceItem);
1803920e296Sopenharmony_ci    } else if (cJSON_IsNumber(valueNode)) {
1813920e296Sopenharmony_ci        return PushString(to_string(valueNode->valueint), resourceItem);
1823920e296Sopenharmony_ci    } else {
1833920e296Sopenharmony_ci        return false;
1843920e296Sopenharmony_ci    }
1853920e296Sopenharmony_ci}
1863920e296Sopenharmony_ci
1873920e296Sopenharmony_cibool JsonCompiler::HandleBoolean(const cJSON *objectNode, ResourceItem &resourceItem) const
1883920e296Sopenharmony_ci{
1893920e296Sopenharmony_ci    cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
1903920e296Sopenharmony_ci    if (cJSON_IsString(valueNode)) {
1913920e296Sopenharmony_ci        regex ref("^\\$(ohos:)?boolean:.*");
1923920e296Sopenharmony_ci        if (!regex_match(valueNode->valuestring, ref)) {
1933920e296Sopenharmony_ci            cerr << "Error: '" << valueNode->valuestring << "' only refer '$boolean:xxx'.";
1943920e296Sopenharmony_ci            cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
1953920e296Sopenharmony_ci            return false;
1963920e296Sopenharmony_ci        }
1973920e296Sopenharmony_ci        return PushString(valueNode->valuestring, resourceItem);
1983920e296Sopenharmony_ci    }
1993920e296Sopenharmony_ci    if (!cJSON_IsBool(valueNode)) {
2003920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value not boolean.";
2013920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
2023920e296Sopenharmony_ci        return false;
2033920e296Sopenharmony_ci    }
2043920e296Sopenharmony_ci    return PushString(cJSON_IsTrue(valueNode) == 1 ? "true" : "false", resourceItem);
2053920e296Sopenharmony_ci}
2063920e296Sopenharmony_ci
2073920e296Sopenharmony_cibool JsonCompiler::HandleColor(const cJSON *objectNode, ResourceItem &resourceItem) const
2083920e296Sopenharmony_ci{
2093920e296Sopenharmony_ci    return HandleString(objectNode, resourceItem);
2103920e296Sopenharmony_ci}
2113920e296Sopenharmony_ci
2123920e296Sopenharmony_cibool JsonCompiler::HandleFloat(const cJSON *objectNode, ResourceItem &resourceItem) const
2133920e296Sopenharmony_ci{
2143920e296Sopenharmony_ci    return HandleString(objectNode, resourceItem);
2153920e296Sopenharmony_ci}
2163920e296Sopenharmony_ci
2173920e296Sopenharmony_cibool JsonCompiler::HandleStringArray(const cJSON *objectNode, ResourceItem &resourceItem) const
2183920e296Sopenharmony_ci{
2193920e296Sopenharmony_ci    vector<string> extra;
2203920e296Sopenharmony_ci    return ParseValueArray(objectNode, resourceItem, extra,
2213920e296Sopenharmony_ci        [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
2223920e296Sopenharmony_ci            if (!cJSON_IsObject(arrayItem)) {
2233920e296Sopenharmony_ci                cerr << "Error: '" << resourceItem.GetName() << "' value array item not object.";
2243920e296Sopenharmony_ci                cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
2253920e296Sopenharmony_ci                return false;
2263920e296Sopenharmony_ci            }
2273920e296Sopenharmony_ci            cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
2283920e296Sopenharmony_ci            if (!CheckJsonStringValue(valueNode, resourceItem)) {
2293920e296Sopenharmony_ci                return false;
2303920e296Sopenharmony_ci            }
2313920e296Sopenharmony_ci            values.push_back(valueNode->valuestring);
2323920e296Sopenharmony_ci            return true;
2333920e296Sopenharmony_ci    });
2343920e296Sopenharmony_ci}
2353920e296Sopenharmony_ci
2363920e296Sopenharmony_cibool JsonCompiler::HandleIntegerArray(const cJSON *objectNode, ResourceItem &resourceItem) const
2373920e296Sopenharmony_ci{
2383920e296Sopenharmony_ci    vector<string> extra;
2393920e296Sopenharmony_ci    return ParseValueArray(objectNode, resourceItem, extra,
2403920e296Sopenharmony_ci        [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) -> bool {
2413920e296Sopenharmony_ci            if (!CheckJsonIntegerValue(arrayItem, resourceItem)) {
2423920e296Sopenharmony_ci                return false;
2433920e296Sopenharmony_ci            }
2443920e296Sopenharmony_ci            if (cJSON_IsString(arrayItem)) {
2453920e296Sopenharmony_ci                values.push_back(arrayItem->valuestring);
2463920e296Sopenharmony_ci            } else {
2473920e296Sopenharmony_ci                values.push_back(to_string(arrayItem->valueint));
2483920e296Sopenharmony_ci            }
2493920e296Sopenharmony_ci            return true;
2503920e296Sopenharmony_ci    });
2513920e296Sopenharmony_ci}
2523920e296Sopenharmony_ci
2533920e296Sopenharmony_cibool JsonCompiler::HandleTheme(const cJSON *objectNode, ResourceItem &resourceItem) const
2543920e296Sopenharmony_ci{
2553920e296Sopenharmony_ci    vector<string> extra;
2563920e296Sopenharmony_ci    if (!ParseParent(objectNode, resourceItem, extra)) {
2573920e296Sopenharmony_ci        return false;
2583920e296Sopenharmony_ci    }
2593920e296Sopenharmony_ci    return ParseValueArray(objectNode, resourceItem, extra,
2603920e296Sopenharmony_ci        [this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
2613920e296Sopenharmony_ci            return ParseAttribute(arrayItem, resourceItem, values);
2623920e296Sopenharmony_ci        });
2633920e296Sopenharmony_ci}
2643920e296Sopenharmony_ci
2653920e296Sopenharmony_cibool JsonCompiler::HandlePattern(const cJSON *objectNode, ResourceItem &resourceItem) const
2663920e296Sopenharmony_ci{
2673920e296Sopenharmony_ci    return HandleTheme(objectNode, resourceItem);
2683920e296Sopenharmony_ci}
2693920e296Sopenharmony_ci
2703920e296Sopenharmony_cibool JsonCompiler::HandlePlural(const cJSON *objectNode, ResourceItem &resourceItem) const
2713920e296Sopenharmony_ci{
2723920e296Sopenharmony_ci    vector<string> extra;
2733920e296Sopenharmony_ci    vector<string> attrs;
2743920e296Sopenharmony_ci    bool result = ParseValueArray(objectNode, resourceItem, extra,
2753920e296Sopenharmony_ci        [&attrs, this](const cJSON *arrayItem, const ResourceItem &resourceItem, vector<string> &values) {
2763920e296Sopenharmony_ci            if (!CheckPluralValue(arrayItem, resourceItem)) {
2773920e296Sopenharmony_ci                return false;
2783920e296Sopenharmony_ci            }
2793920e296Sopenharmony_ci            cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
2803920e296Sopenharmony_ci            if (!quantityNode || !cJSON_IsString(quantityNode)) {
2813920e296Sopenharmony_ci                return false;
2823920e296Sopenharmony_ci            }
2833920e296Sopenharmony_ci            string quantityValue = quantityNode->valuestring;
2843920e296Sopenharmony_ci            if (find(attrs.begin(), attrs.end(), quantityValue) != attrs.end()) {
2853920e296Sopenharmony_ci                cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
2863920e296Sopenharmony_ci                cerr << "' duplicated." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
2873920e296Sopenharmony_ci                return false;
2883920e296Sopenharmony_ci            }
2893920e296Sopenharmony_ci            attrs.push_back(quantityValue);
2903920e296Sopenharmony_ci            values.push_back(quantityValue);
2913920e296Sopenharmony_ci            cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
2923920e296Sopenharmony_ci            if (!valueNode || !cJSON_IsString(valueNode)) {
2933920e296Sopenharmony_ci                return false;
2943920e296Sopenharmony_ci            }
2953920e296Sopenharmony_ci            values.push_back(valueNode->valuestring);
2963920e296Sopenharmony_ci            return true;
2973920e296Sopenharmony_ci        });
2983920e296Sopenharmony_ci    if (!result) {
2993920e296Sopenharmony_ci        return false;
3003920e296Sopenharmony_ci    }
3013920e296Sopenharmony_ci    if (find(attrs.begin(), attrs.end(), "other") == attrs.end()) {
3023920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity must contains 'other'.";
3033920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3043920e296Sopenharmony_ci        return false;
3053920e296Sopenharmony_ci    }
3063920e296Sopenharmony_ci    return true;
3073920e296Sopenharmony_ci}
3083920e296Sopenharmony_ci
3093920e296Sopenharmony_cibool JsonCompiler::HandleSymbol(const cJSON *objectNode, ResourceItem &resourceItem) const
3103920e296Sopenharmony_ci{
3113920e296Sopenharmony_ci    cJSON *valueNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
3123920e296Sopenharmony_ci    if (!CheckJsonSymbolValue(valueNode, resourceItem)) {
3133920e296Sopenharmony_ci        return false;
3143920e296Sopenharmony_ci    }
3153920e296Sopenharmony_ci    return PushString(valueNode->valuestring, resourceItem);
3163920e296Sopenharmony_ci}
3173920e296Sopenharmony_ci
3183920e296Sopenharmony_cibool JsonCompiler::PushString(const string &value, ResourceItem &resourceItem) const
3193920e296Sopenharmony_ci{
3203920e296Sopenharmony_ci    if (!resourceItem.SetData(reinterpret_cast<const int8_t *>(value.c_str()), value.length())) {
3213920e296Sopenharmony_ci        cerr << "Error: resourceItem setdata fail,'" << resourceItem.GetName() << "'.";
3223920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3233920e296Sopenharmony_ci        return false;
3243920e296Sopenharmony_ci    }
3253920e296Sopenharmony_ci    return true;
3263920e296Sopenharmony_ci}
3273920e296Sopenharmony_ci
3283920e296Sopenharmony_cibool JsonCompiler::CheckJsonStringValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
3293920e296Sopenharmony_ci{
3303920e296Sopenharmony_ci    if (!valueNode || !cJSON_IsString(valueNode)) {
3313920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value not string.";
3323920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3333920e296Sopenharmony_ci        return false;
3343920e296Sopenharmony_ci    }
3353920e296Sopenharmony_ci
3363920e296Sopenharmony_ci    const map<ResType, string> REFS = {
3373920e296Sopenharmony_ci        { ResType::STRING, "\\$(ohos:)?string:" },
3383920e296Sopenharmony_ci        { ResType::STRARRAY, "\\$(ohos:)?string:" },
3393920e296Sopenharmony_ci        { ResType::COLOR, "\\$(ohos:)?color:" },
3403920e296Sopenharmony_ci        { ResType::FLOAT, "\\$(ohos:)?float:" }
3413920e296Sopenharmony_ci    };
3423920e296Sopenharmony_ci
3433920e296Sopenharmony_ci    string value = valueNode->valuestring;
3443920e296Sopenharmony_ci    ResType type = resourceItem.GetResType();
3453920e296Sopenharmony_ci    if (type ==  ResType::COLOR && !CheckColorValue(value.c_str())) {
3463920e296Sopenharmony_ci        string error = "invalid color value '" + value + \
3473920e296Sopenharmony_ci                        "', only support refer '$color:xxx' or '#rgb','#argb','#rrggbb','#aarrggbb'.";
3483920e296Sopenharmony_ci        cerr << "Error: " << error << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3493920e296Sopenharmony_ci        return false;
3503920e296Sopenharmony_ci    }
3513920e296Sopenharmony_ci    regex ref("^\\$.+:");
3523920e296Sopenharmony_ci    smatch result;
3533920e296Sopenharmony_ci    if (regex_search(value, result, ref) && !regex_match(result[0].str(), regex(REFS.at(type)))) {
3543920e296Sopenharmony_ci        cerr << "Error: '" << value << "', only refer '"<< REFS.at(type) << "xxx'.";
3553920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3563920e296Sopenharmony_ci        return false;
3573920e296Sopenharmony_ci    }
3583920e296Sopenharmony_ci    return true;
3593920e296Sopenharmony_ci}
3603920e296Sopenharmony_ci
3613920e296Sopenharmony_cibool JsonCompiler::CheckJsonIntegerValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
3623920e296Sopenharmony_ci{
3633920e296Sopenharmony_ci    if (!valueNode) {
3643920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value is empty";
3653920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3663920e296Sopenharmony_ci        return false;
3673920e296Sopenharmony_ci    }
3683920e296Sopenharmony_ci    if (cJSON_IsString(valueNode)) {
3693920e296Sopenharmony_ci        regex ref("^\\$(ohos:)?integer:.*");
3703920e296Sopenharmony_ci        if (!regex_match(valueNode->valuestring, ref)) {
3713920e296Sopenharmony_ci            cerr << "Error: '" << valueNode->valuestring << "', only refer '$integer:xxx'.";
3723920e296Sopenharmony_ci            cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3733920e296Sopenharmony_ci            return false;
3743920e296Sopenharmony_ci        }
3753920e296Sopenharmony_ci    } else if (!ResourceUtil::IsIntValue(valueNode)) {
3763920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value not integer.";
3773920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3783920e296Sopenharmony_ci        return false;
3793920e296Sopenharmony_ci    }
3803920e296Sopenharmony_ci    return true;
3813920e296Sopenharmony_ci}
3823920e296Sopenharmony_ci
3833920e296Sopenharmony_cibool JsonCompiler::CheckJsonSymbolValue(const cJSON *valueNode, const ResourceItem &resourceItem) const
3843920e296Sopenharmony_ci{
3853920e296Sopenharmony_ci    if (!valueNode || !cJSON_IsString(valueNode)) {
3863920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value not string.";
3873920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3883920e296Sopenharmony_ci        return false;
3893920e296Sopenharmony_ci    }
3903920e296Sopenharmony_ci    string unicodeStr = valueNode->valuestring;
3913920e296Sopenharmony_ci    if (regex_match(unicodeStr, regex("^\\$(ohos:)?symbol:.*"))) {
3923920e296Sopenharmony_ci        return true;
3933920e296Sopenharmony_ci    }
3943920e296Sopenharmony_ci    int unicode = strtol(unicodeStr.c_str(), nullptr, 16);
3953920e296Sopenharmony_ci    if (!ResourceUtil::isUnicodeInPlane15or16(unicode)) {
3963920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value must in 0xF0000 ~ 0xFFFFF or 0x100000 ~ 0x10FFFF.";
3973920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
3983920e296Sopenharmony_ci        return false;
3993920e296Sopenharmony_ci    }
4003920e296Sopenharmony_ci    return true;
4013920e296Sopenharmony_ci}
4023920e296Sopenharmony_ci
4033920e296Sopenharmony_cibool JsonCompiler::ParseValueArray(const cJSON *objectNode, ResourceItem &resourceItem,
4043920e296Sopenharmony_ci                                   const vector<string> &extra, HandleValue callback) const
4053920e296Sopenharmony_ci{
4063920e296Sopenharmony_ci    cJSON *arrayNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
4073920e296Sopenharmony_ci    if (!cJSON_IsArray(arrayNode)) {
4083920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value not array.";
4093920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4103920e296Sopenharmony_ci        return false;
4113920e296Sopenharmony_ci    }
4123920e296Sopenharmony_ci
4133920e296Sopenharmony_ci    if (cJSON_GetArraySize(arrayNode) == 0) {
4143920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' value empty.";
4153920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4163920e296Sopenharmony_ci        return false;
4173920e296Sopenharmony_ci    }
4183920e296Sopenharmony_ci
4193920e296Sopenharmony_ci    vector<string> contents;
4203920e296Sopenharmony_ci    if (!extra.empty()) {
4213920e296Sopenharmony_ci        contents.assign(extra.begin(), extra.end());
4223920e296Sopenharmony_ci    }
4233920e296Sopenharmony_ci    for (cJSON *item = arrayNode->child; item; item = item->next) {
4243920e296Sopenharmony_ci        vector<string> values;
4253920e296Sopenharmony_ci        if (!callback(item, resourceItem, values)) {
4263920e296Sopenharmony_ci            return false;
4273920e296Sopenharmony_ci        }
4283920e296Sopenharmony_ci        contents.insert(contents.end(), values.begin(), values.end());
4293920e296Sopenharmony_ci    }
4303920e296Sopenharmony_ci
4313920e296Sopenharmony_ci    string data = ResourceUtil::ComposeStrings(contents);
4323920e296Sopenharmony_ci    if (data.empty()) {
4333920e296Sopenharmony_ci        cerr << "Error: '" << resourceItem.GetName() << "' array too large.";
4343920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4353920e296Sopenharmony_ci        return false;
4363920e296Sopenharmony_ci    }
4373920e296Sopenharmony_ci    return PushString(data, resourceItem);
4383920e296Sopenharmony_ci}
4393920e296Sopenharmony_ci
4403920e296Sopenharmony_cibool JsonCompiler::ParseParent(const cJSON *objectNode, const ResourceItem &resourceItem,
4413920e296Sopenharmony_ci                               vector<string> &extra) const
4423920e296Sopenharmony_ci{
4433920e296Sopenharmony_ci    cJSON *parentNode = cJSON_GetObjectItem(objectNode, TAG_PARENT.c_str());
4443920e296Sopenharmony_ci    string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
4453920e296Sopenharmony_ci    if (parentNode) {
4463920e296Sopenharmony_ci        if (!cJSON_IsString(parentNode)) {
4473920e296Sopenharmony_ci            cerr << "Error: " << type << " '" << resourceItem.GetName() << "' parent not string.";
4483920e296Sopenharmony_ci            cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4493920e296Sopenharmony_ci            return false;
4503920e296Sopenharmony_ci        }
4513920e296Sopenharmony_ci        string parentValue = parentNode->valuestring;
4523920e296Sopenharmony_ci        if (parentValue.empty()) {
4533920e296Sopenharmony_ci            cerr << "Error: " << type << " '"<< resourceItem.GetName() << "' parent empty.";
4543920e296Sopenharmony_ci            cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4553920e296Sopenharmony_ci            return false;
4563920e296Sopenharmony_ci        }
4573920e296Sopenharmony_ci        if (regex_match(parentValue, regex("^ohos:" + type + ":.+"))) {
4583920e296Sopenharmony_ci            parentValue = "$" + parentValue;
4593920e296Sopenharmony_ci        } else {
4603920e296Sopenharmony_ci            parentValue = "$" + type + ":" + parentValue;
4613920e296Sopenharmony_ci        }
4623920e296Sopenharmony_ci        extra.push_back(parentValue);
4633920e296Sopenharmony_ci    }
4643920e296Sopenharmony_ci    return true;
4653920e296Sopenharmony_ci}
4663920e296Sopenharmony_ci
4673920e296Sopenharmony_cibool JsonCompiler::ParseAttribute(const cJSON *arrayItem, const ResourceItem &resourceItem,
4683920e296Sopenharmony_ci                                  vector<string> &values) const
4693920e296Sopenharmony_ci{
4703920e296Sopenharmony_ci    string type = ResourceUtil::ResTypeToString(resourceItem.GetResType());
4713920e296Sopenharmony_ci    if (!arrayItem || !cJSON_IsObject(arrayItem)) {
4723920e296Sopenharmony_ci        cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute not object.";
4733920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4743920e296Sopenharmony_ci        return false;
4753920e296Sopenharmony_ci    }
4763920e296Sopenharmony_ci    cJSON *nameNode = cJSON_GetObjectItem(arrayItem, TAG_NAME.c_str());
4773920e296Sopenharmony_ci    if (!nameNode) {
4783920e296Sopenharmony_ci        cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute name empty.";
4793920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4803920e296Sopenharmony_ci        return false;
4813920e296Sopenharmony_ci    }
4823920e296Sopenharmony_ci    if (!cJSON_IsString(nameNode)) {
4833920e296Sopenharmony_ci        cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute name not string.";
4843920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4853920e296Sopenharmony_ci        return false;
4863920e296Sopenharmony_ci    }
4873920e296Sopenharmony_ci    values.push_back(nameNode->valuestring);
4883920e296Sopenharmony_ci
4893920e296Sopenharmony_ci    cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
4903920e296Sopenharmony_ci    if (!valueNode) {
4913920e296Sopenharmony_ci        cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute '" << nameNode->valuestring;
4923920e296Sopenharmony_ci        cerr << "' value empty." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4933920e296Sopenharmony_ci        return false;
4943920e296Sopenharmony_ci    }
4953920e296Sopenharmony_ci    if (!cJSON_IsString(valueNode)) {
4963920e296Sopenharmony_ci        cerr << "Error: " << type << " '" << resourceItem.GetName() << "' attribute '" << nameNode->valuestring;
4973920e296Sopenharmony_ci        cerr << "' value not string." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
4983920e296Sopenharmony_ci        return false;
4993920e296Sopenharmony_ci    }
5003920e296Sopenharmony_ci    values.push_back(valueNode->valuestring);
5013920e296Sopenharmony_ci    return true;
5023920e296Sopenharmony_ci}
5033920e296Sopenharmony_ci
5043920e296Sopenharmony_cibool JsonCompiler::CheckPluralValue(const cJSON *arrayItem, const ResourceItem &resourceItem) const
5053920e296Sopenharmony_ci{
5063920e296Sopenharmony_ci    if (!arrayItem || !cJSON_IsObject(arrayItem)) {
5073920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' array item not object.";
5083920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
5093920e296Sopenharmony_ci        return false;
5103920e296Sopenharmony_ci    }
5113920e296Sopenharmony_ci    cJSON *quantityNode = cJSON_GetObjectItem(arrayItem, TAG_QUANTITY.c_str());
5123920e296Sopenharmony_ci    if (!quantityNode) {
5133920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity empty.";
5143920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
5153920e296Sopenharmony_ci        return false;
5163920e296Sopenharmony_ci    }
5173920e296Sopenharmony_ci    if (!cJSON_IsString(quantityNode)) {
5183920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity not string.";
5193920e296Sopenharmony_ci        cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
5203920e296Sopenharmony_ci        return false;
5213920e296Sopenharmony_ci    }
5223920e296Sopenharmony_ci    string quantityValue = quantityNode->valuestring;
5233920e296Sopenharmony_ci    if (find(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), quantityValue) == QUANTITY_ATTRS.end()) {
5243920e296Sopenharmony_ci        string buffer(" ");
5253920e296Sopenharmony_ci        for_each(QUANTITY_ATTRS.begin(), QUANTITY_ATTRS.end(), [&buffer](auto iter) {
5263920e296Sopenharmony_ci                buffer.append(iter).append(" ");
5273920e296Sopenharmony_ci            });
5283920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
5293920e296Sopenharmony_ci        cerr << "' not in [" << buffer << "]." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
5303920e296Sopenharmony_ci        return false;
5313920e296Sopenharmony_ci    }
5323920e296Sopenharmony_ci
5333920e296Sopenharmony_ci    cJSON *valueNode = cJSON_GetObjectItem(arrayItem, TAG_VALUE.c_str());
5343920e296Sopenharmony_ci    if (!valueNode) {
5353920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
5363920e296Sopenharmony_ci        cerr << "' value empty." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
5373920e296Sopenharmony_ci        return false;
5383920e296Sopenharmony_ci    }
5393920e296Sopenharmony_ci    if (!cJSON_IsString(valueNode)) {
5403920e296Sopenharmony_ci        cerr << "Error: Plural '" << resourceItem.GetName() << "' quantity '" << quantityValue;
5413920e296Sopenharmony_ci        cerr << "' value not string." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
5423920e296Sopenharmony_ci        return false;
5433920e296Sopenharmony_ci    }
5443920e296Sopenharmony_ci    return true;
5453920e296Sopenharmony_ci}
5463920e296Sopenharmony_ci
5473920e296Sopenharmony_cibool JsonCompiler::CheckColorValue(const char *s) const
5483920e296Sopenharmony_ci{
5493920e296Sopenharmony_ci    if (s == nullptr) {
5503920e296Sopenharmony_ci        return false;
5513920e296Sopenharmony_ci    }
5523920e296Sopenharmony_ci    // color regex
5533920e296Sopenharmony_ci    string regColor = "^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$";
5543920e296Sopenharmony_ci    if (regex_match(s, regex("^\\$.*")) || regex_match(s, regex(regColor))) {
5553920e296Sopenharmony_ci        return true;
5563920e296Sopenharmony_ci    }
5573920e296Sopenharmony_ci    return false;
5583920e296Sopenharmony_ci}
5593920e296Sopenharmony_ci}
5603920e296Sopenharmony_ci}
5613920e296Sopenharmony_ci}
562