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 #include "translatable_parser.h"
17 #include <algorithm>
18 #include <iostream>
19 #include "resource_util.h"
20 
21 namespace OHOS {
22 namespace Global {
23 namespace Restool {
24 using namespace std;
25 const string TAG_VALUE = "value";
26 const string TAG_ATTR = "attr";
27 const string TAG_TRANSLATABLE = "translatable";
28 const string TAG_PRIORITY = "priority";
29 const string NO_TRANSLATE_START = "{noTranslateStart}";
30 const string NO_TRANSLATE_END = "{noTranslateEnd}";
31 const vector<string> PRIORITY_ATTRS = { "code", "translate", "LT", "customer" };
32 
ParseTranslatable(cJSON *objectNode, const FileInfo &fileInfo, const string &name)33 bool TranslatableParse::ParseTranslatable(cJSON *objectNode, const FileInfo &fileInfo, const string &name)
34 {
35     if (fileInfo.fileType == ResType::STRING) {
36         return ParseTranslatable(objectNode, fileInfo.filePath);
37     }
38     cJSON *arrayNode = cJSON_GetObjectItem(objectNode, TAG_VALUE.c_str());
39     if (!arrayNode || !cJSON_IsArray(arrayNode)) {
40         cerr << "Error: '" << name << "' value not array.";
41         cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
42         return false;
43     }
44 
45     if (cJSON_GetArraySize(arrayNode) == 0) {
46         cerr << "Error: '" << name << "' value empty.";
47         cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
48         return false;
49     }
50     int32_t index = -1;
51     for (cJSON *item = arrayNode->child; item; item = item->next) {
52         index++;
53         if (!item || !cJSON_IsObject(item)) {
54             cerr << "Error: '" << name << "' value the seq=" << index << " item must be object.";
55             cerr << NEW_LINE_PATH << fileInfo.filePath << endl;
56             return false;
57         }
58 
59         if (!ParseTranslatable(item, fileInfo.filePath)) {
60             return false;
61         }
62     }
63     return true;
64 }
65 
ParseTranslatable(cJSON *objectNode, const string &filePath)66 bool TranslatableParse::ParseTranslatable(cJSON *objectNode, const string &filePath)
67 {
68     if (!CheckBaseStringAttr(objectNode)) {
69         cerr << NEW_LINE_PATH << filePath << endl;
70         return false;
71     }
72     if (!ReplaceTranslateTags(objectNode, TAG_VALUE.c_str())) {
73         cerr << NEW_LINE_PATH << filePath << endl;
74         return false;
75     }
76     return true;
77 }
78 
CheckBaseStringAttr(const cJSON *objectNode)79 bool TranslatableParse::CheckBaseStringAttr(const cJSON *objectNode)
80 {
81     cJSON *attrNode = cJSON_GetObjectItem(objectNode, TAG_ATTR.c_str());
82     if (!attrNode) {
83         return true;
84     }
85 
86     if (!cJSON_IsObject(attrNode)) {
87         cerr << "Error: attr node not obeject.";
88         return false;
89     }
90     return CheckBaseStringTranslatable(attrNode);
91 }
92 
CheckBaseStringTranslatable(const cJSON *attrNode)93 bool TranslatableParse::CheckBaseStringTranslatable(const cJSON *attrNode)
94 {
95     cJSON *translatableNode = cJSON_GetObjectItem(attrNode, TAG_TRANSLATABLE.c_str());
96     if (!translatableNode) {
97         return CheckBaseStringPriority(attrNode);
98     }
99     if (!cJSON_IsBool(translatableNode)) {
100         cerr << "Error: invalid value for '" << TAG_TRANSLATABLE << "'. Must be a boolean.";
101         return false;
102     }
103     return CheckBaseStringPriority(attrNode);
104 }
105 
CheckBaseStringPriority(const cJSON *attrNode)106 bool TranslatableParse::CheckBaseStringPriority(const cJSON *attrNode)
107 {
108     cJSON *priorityNode = cJSON_GetObjectItem(attrNode, TAG_PRIORITY.c_str());
109     if (!priorityNode) {
110         return true;
111     }
112     if (!cJSON_IsString(priorityNode)) {
113         cerr << "Error: invalid value for '" << TAG_PRIORITY << "'. Must be a string.";
114         return false;
115     }
116     string priorityValue = priorityNode->valuestring;
117     if (find(PRIORITY_ATTRS.begin(), PRIORITY_ATTRS.end(), priorityValue) == PRIORITY_ATTRS.end()) {
118         string message("[ ");
119         for (const auto &value : PRIORITY_ATTRS) {
120             message.append("'" + value + "' ");
121         }
122         message.append("]");
123         cerr << "Error: invalid value for '" << TAG_PRIORITY << "'. Must in " << message;
124         return false;
125     }
126     return true;
127 }
128 
ReplaceTranslateTags(cJSON *node, const char *key)129 bool TranslatableParse::ReplaceTranslateTags(cJSON *node, const char *key)
130 {
131     cJSON *valueNode = cJSON_GetObjectItem(node, TAG_VALUE.c_str());
132     if (!valueNode || !cJSON_IsString(valueNode)) {
133         cerr << "Error: value not string.";
134         return false;
135     }
136 
137     string value = valueNode->valuestring;
138     if (GetReplaceStringTranslate(value)) {
139         return cJSON_ReplaceItemInObject(node, key, cJSON_CreateString(value.c_str()));
140     }
141     return true;
142 }
143 
GetReplaceStringTranslate(string &str)144 bool TranslatableParse::GetReplaceStringTranslate(string &str)
145 {
146     vector<size_t> posData;
147     if (!FindTranslatePairs(str, posData) || posData.empty()) {
148         return false;
149     }
150     size_t startIndex = 0;
151     string tmp;
152     for (size_t index = 0; index < posData.size(); index++) {
153         tmp.append(str, startIndex, posData[index] - startIndex);
154         startIndex = posData[++index];
155     }
156     str = tmp.append(str, posData.back());
157     return true;
158 }
159 
FindTranslatePairs(const string &str, vector<size_t> &posData)160 bool TranslatableParse::FindTranslatePairs(const string &str, vector<size_t> &posData)
161 {
162     auto startPos = str.find(NO_TRANSLATE_START, 0);
163     auto endPos = str.find(NO_TRANSLATE_END, 0);
164     auto startLength = NO_TRANSLATE_START.length();
165     auto endLength = NO_TRANSLATE_END.length();
166     while (!(startPos == string::npos && endPos == string::npos)) {
167         if (startPos == string::npos || endPos == string::npos) {
168             return false;
169         }
170         if (startPos >= endPos || (!posData.empty() && posData.back() >= startPos)) {
171             return false;
172         }
173         posData.emplace_back(startPos);
174         posData.emplace_back(startPos + startLength);
175         posData.emplace_back(endPos);
176         posData.emplace_back(endPos + endLength);
177         startPos = str.find(NO_TRANSLATE_START, startPos + startLength);
178         endPos = str.find(NO_TRANSLATE_END, endPos + endLength);
179     }
180     return true;
181 }
182 }
183 }
184 }
185