1 /*
2 * Copyright (c) 2021 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 "reference_parser.h"
17 #include<iostream>
18 #include<regex>
19 #include "file_entry.h"
20 #include "restool_errors.h"
21 #include "xml_key_node.h"
22
23 namespace OHOS {
24 namespace Global {
25 namespace Restool {
26 using namespace std;
27 const map<string, ResType> ReferenceParser::ID_REFS = {
28 { "^\\$id:", ResType::ID },
29 { "^\\$boolean:", ResType::BOOLEAN },
30 { "^\\$color:", ResType::COLOR },
31 { "^\\$float:", ResType::FLOAT },
32 { "^\\$media:", ResType::MEDIA },
33 { "^\\$profile:", ResType::PROF },
34 { "^\\$integer:", ResType::INTEGER },
35 { "^\\$string:", ResType::STRING },
36 { "^\\$layout:", ResType::LAYOUT },
37 { "^\\$pattern:", ResType::PATTERN },
38 { "^\\$plural:", ResType::PLURAL },
39 { "^\\$graphic:", ResType::GRAPHIC },
40 { "^\\$theme:", ResType::THEME }
41 };
42
43 const map<string, ResType> ReferenceParser::ID_OHOS_REFS = {
44 { "^\\$ohos:id:", ResType::ID },
45 { "^\\$ohos:boolean:", ResType::BOOLEAN },
46 { "^\\$ohos:color:", ResType::COLOR },
47 { "^\\$ohos:float:", ResType::FLOAT },
48 { "^\\$ohos:media:", ResType::MEDIA },
49 { "^\\$ohos:profile:", ResType::PROF },
50 { "^\\$ohos:integer:", ResType::INTEGER },
51 { "^\\$ohos:string:", ResType::STRING },
52 { "^\\$ohos:layout:", ResType::LAYOUT },
53 { "^\\$ohos:pattern:", ResType::PATTERN },
54 { "^\\$ohos:plural:", ResType::PLURAL },
55 { "^\\$ohos:graphic:", ResType::GRAPHIC },
56 { "^\\$ohos:theme:", ResType::THEME }
57 };
58
ReferenceParser()59 ReferenceParser::ReferenceParser() : idWorker_(IdWorker::GetInstance())
60 {
61 }
62
~ReferenceParser()63 ReferenceParser::~ReferenceParser()
64 {
65 }
66
ParseRefInSolidXml(const vector<string> & solidXmlFolders) const67 uint32_t ReferenceParser::ParseRefInSolidXml(const vector<string> &solidXmlFolders) const
68 {
69 for (const auto &solidXmlFolder : solidXmlFolders) {
70 string filePath = FileEntry::FilePath(solidXmlFolder)
71 .Append(XmlKeyNode::KEY_TO_FILE_NAME.at(XmlKeyNode::KeyType::CONSTANT)).GetPath();
72 if (!ResourceUtil::FileExist(filePath)) {
73 continue;
74 }
75
76 XmlKeyNode xmlKeyNode;
77 if (!xmlKeyNode.LoadFromFile(filePath, [this](auto &key) -> bool {
78 return ParseRefString(key);
79 })) {
80 return RESTOOL_ERROR;
81 }
82
83 if (!xmlKeyNode.SaveToFile(filePath)) {
84 return RESTOOL_ERROR;
85 }
86 }
87 return RESTOOL_SUCCESS;
88 }
89
ParseRefInElement(map<int32_t,vector<ResourceItem>> & items) const90 uint32_t ReferenceParser::ParseRefInElement(map<int32_t, vector<ResourceItem>> &items) const
91 {
92 for (auto &iter : items) {
93 for (auto &resourceItem : iter.second) {
94 if (IsNotElement(resourceItem.GetResType())) {
95 break;
96 }
97 if (!ParseRefResourceItem(resourceItem)) {
98 return RESTOOL_ERROR;
99 }
100 }
101 }
102 return RESTOOL_SUCCESS;
103 }
104
ParseRefInString(string & value,bool & update) const105 uint32_t ReferenceParser::ParseRefInString(string &value, bool &update) const
106 {
107 if (ParseRefString(value, update)) {
108 return RESTOOL_SUCCESS;
109 }
110 return RESTOOL_ERROR;
111 }
112
ParseRefInProfile(const string & output) const113 uint32_t ReferenceParser::ParseRefInProfile(const string &output) const
114 {
115 string profileFolder = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append("base").Append("profile").GetPath();
116 if (!ResourceUtil::FileExist(profileFolder)) {
117 return RESTOOL_SUCCESS;
118 }
119
120 FileEntry f(profileFolder);
121 for (const auto &entry : f.GetChilds()) {
122 if (!entry->IsFile()) {
123 cerr << "Error: '" << entry->GetFilePath().GetPath() << "' is directory." << endl;
124 return false;
125 }
126
127 if (entry->GetFilePath().GetExtension() != ".json") {
128 continue;
129 }
130
131 if (ParseRefInJson(entry->GetFilePath().GetPath()) != RESTOOL_SUCCESS) {
132 return RESTOOL_ERROR;
133 }
134 }
135 return RESTOOL_SUCCESS;
136 }
137
ParseRefInJson(const string & filePath) const138 uint32_t ReferenceParser::ParseRefInJson(const string &filePath) const
139 {
140 Json::Value root;
141 if (!ResourceUtil::OpenJsonFile(filePath, root)) {
142 return RESTOOL_ERROR;
143 }
144
145 if (!ParseRefJsonImpl(root)) {
146 return RESTOOL_ERROR;
147 }
148
149 if (!ResourceUtil::SaveToJsonFile(filePath, root)) {
150 return RESTOOL_ERROR;
151 }
152 return RESTOOL_SUCCESS;
153 }
154
ParseRefResourceItem(ResourceItem & resourceItem) const155 bool ReferenceParser::ParseRefResourceItem(ResourceItem &resourceItem) const
156 {
157 ResType resType = resourceItem.GetResType();
158 string data;
159 bool update = false;
160 if (IsStringOfResourceItem(resType)) {
161 data = string(reinterpret_cast<const char *>(resourceItem.GetData()), resourceItem.GetDataLength());
162 if (!ParseRefString(data, update)) {
163 cerr << "Error: in " << resourceItem.GetFilePath() << endl;
164 return false;
165 }
166 if (!update) {
167 return true;
168 }
169 } else if (IsArrayOfResourceItem(resType)) {
170 if (!ParseRefResourceItemData(resourceItem, data, update)) {
171 return false;
172 }
173 if (!update) {
174 return true;
175 }
176 }
177 if (update && !resourceItem.SetData(reinterpret_cast<const int8_t *>(data.c_str()), data.length())) {
178 cerr << "Error: set data fail '" << resourceItem.GetName() << "' in " << resourceItem.GetFilePath() << endl;
179 return false;
180 }
181 return true;
182 }
183
ParseRefResourceItemData(const ResourceItem & resourceItem,string & data,bool & update) const184 bool ReferenceParser::ParseRefResourceItemData(const ResourceItem &resourceItem, string &data, bool &update) const
185 {
186 data = string(reinterpret_cast<const char *>(resourceItem.GetData()), resourceItem.GetDataLength());
187 vector<string> contents = ResourceUtil::DecomposeStrings(data);
188 if (contents.empty()) {
189 cerr << "Error: DecomposeStrings fail '" << resourceItem.GetName() << "' in ";
190 cerr << resourceItem.GetFilePath() << endl;
191 return false;
192 }
193
194 for (auto &content : contents) {
195 bool flag = false;
196 if (!ParseRefString(content, flag)) {
197 cerr << "Error: in " << resourceItem.GetFilePath() << endl;
198 return false;
199 }
200 update = (update || flag);
201 }
202
203 if (!update) {
204 return true;
205 }
206
207 data = ResourceUtil::ComposeStrings(contents);
208 if (data.empty()) {
209 cerr << "Error: ComposeStrings fail '" << resourceItem.GetName() << "' in ";
210 cerr << resourceItem.GetFilePath() << endl;
211 return false;
212 }
213 return true;
214 }
215
IsStringOfResourceItem(ResType resType) const216 bool ReferenceParser::IsStringOfResourceItem(ResType resType) const
217 {
218 if (resType == ResType::STRING ||
219 resType == ResType::INTEGER ||
220 resType == ResType::BOOLEAN ||
221 resType == ResType::COLOR ||
222 resType == ResType::FLOAT) {
223 return true;
224 }
225 return false;
226 }
227
IsArrayOfResourceItem(ResType resType) const228 bool ReferenceParser::IsArrayOfResourceItem(ResType resType) const
229 {
230 if (resType == ResType::STRARRAY ||
231 resType == ResType::INTARRAY ||
232 resType == ResType::PLURAL ||
233 resType == ResType::THEME ||
234 resType == ResType::PATTERN) {
235 return true;
236 }
237 return false;
238 }
239
IsNotElement(ResType resType) const240 bool ReferenceParser::IsNotElement(ResType resType) const
241 {
242 auto result = find_if(g_contentClusterMap.begin(), g_contentClusterMap.end(), [resType](const auto &iter) {
243 return resType == iter.second;
244 });
245 if (result == g_contentClusterMap.end()) {
246 return true;
247 }
248 return false;
249 }
250
ParseRefString(string & key) const251 bool ReferenceParser::ParseRefString(string &key) const
252 {
253 bool update = false;
254 return ParseRefString(key, update);
255 }
256
ParseRefString(std::string & key,bool & update) const257 bool ReferenceParser::ParseRefString(std::string &key, bool &update) const
258 {
259 update = false;
260 if (regex_match(key, regex("^\\$ohos:[a-z]+:.+"))) {
261 update = true;
262 return ParseRefImpl(key, ID_OHOS_REFS, true);
263 } else if (regex_match(key, regex("^\\$[a-z]+:.+"))) {
264 update = true;
265 return ParseRefImpl(key, ID_REFS, false);
266 }
267 return true;
268 }
269
ParseRefImpl(string & key,const map<string,ResType> & refs,bool isSystem) const270 bool ReferenceParser::ParseRefImpl(string &key, const map<string, ResType> &refs, bool isSystem) const
271 {
272 for (const auto &ref : refs) {
273 smatch result;
274 if (regex_search(key, result, regex(ref.first))) {
275 string name = key.substr(result[0].str().length());
276 int32_t id = idWorker_.GetId(ref.second, name);
277 if (isSystem) {
278 id = idWorker_.GetSystemId(ref.second, name);
279 } else {
280 id = idWorker_.GetId(ref.second, name);
281 }
282 if (id < 0) {
283 cerr << "Error: ref '" << key << "' don't be defined." << endl;
284 return false;
285 }
286
287 key = to_string(id);
288 if (ref.second != ResType::ID) {
289 key = key = "$" + ResourceUtil::ResTypeToString(ref.second) + ":" + to_string(id);
290 }
291 return true;
292 }
293 }
294 cerr << "Error: reference '" << key << "' invalid." << endl;
295 return false;
296 }
297
ParseRefJsonImpl(Json::Value & node) const298 bool ReferenceParser::ParseRefJsonImpl(Json::Value &node) const
299 {
300 if (node.isObject()) {
301 for (const auto &member : node.getMemberNames()) {
302 if (!ParseRefJsonImpl(node[member])) {
303 return false;
304 }
305 }
306 } else if (node.isArray()) {
307 for (Json::ArrayIndex i = 0; i < node.size(); i++) {
308 if (!ParseRefJsonImpl(node[i])) {
309 return false;
310 }
311 }
312 } else if (node.isString()) {
313 string value = node.asString();
314 if (!ParseRefString(value)) {
315 return false;
316 }
317 node = value;
318 }
319 return true;
320 }
321 }
322 }
323 }
324