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