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