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
22 namespace OHOS {
23 namespace Global {
24 namespace Restool {
25 using namespace std;
26 const map<string, ResType> ReferenceParser::ID_REFS = {
27 { "^\\$id:", ResType::ID },
28 { "^\\$boolean:", ResType::BOOLEAN },
29 { "^\\$color:", ResType::COLOR },
30 { "^\\$float:", ResType::FLOAT },
31 { "^\\$media:", ResType::MEDIA },
32 { "^\\$profile:", ResType::PROF },
33 { "^\\$integer:", ResType::INTEGER },
34 { "^\\$string:", ResType::STRING },
35 { "^\\$pattern:", ResType::PATTERN },
36 { "^\\$plural:", ResType::PLURAL },
37 { "^\\$theme:", ResType::THEME },
38 { "^\\$symbol:", ResType::SYMBOL }
39 };
40
41 const map<string, ResType> ReferenceParser::ID_OHOS_REFS = {
42 { "^\\$ohos:id:", ResType::ID },
43 { "^\\$ohos:boolean:", ResType::BOOLEAN },
44 { "^\\$ohos:color:", ResType::COLOR },
45 { "^\\$ohos:float:", ResType::FLOAT },
46 { "^\\$ohos:media:", ResType::MEDIA },
47 { "^\\$ohos:profile:", ResType::PROF },
48 { "^\\$ohos:integer:", ResType::INTEGER },
49 { "^\\$ohos:string:", ResType::STRING },
50 { "^\\$ohos:pattern:", ResType::PATTERN },
51 { "^\\$ohos:plural:", ResType::PLURAL },
52 { "^\\$ohos:theme:", ResType::THEME },
53 { "^\\$ohos:symbol:", ResType::SYMBOL }
54 };
55
ReferenceParser()56 ReferenceParser::ReferenceParser() : idWorker_(IdWorker::GetInstance())
57 {
58 }
59
~ReferenceParser()60 ReferenceParser::~ReferenceParser()
61 {
62 }
63
ParseRefInResources(map<int32_t,vector<ResourceItem>> & items,const string & output)64 uint32_t ReferenceParser::ParseRefInResources(map<int32_t, vector<ResourceItem>> &items, const string &output)
65 {
66 for (auto &iter : items) {
67 for (auto &resourceItem : iter.second) {
68 if (IsElementRef(resourceItem) && ParseRefInResourceItem(resourceItem) != RESTOOL_SUCCESS) {
69 return RESTOOL_ERROR;
70 }
71 if ((IsMediaRef(resourceItem) || IsProfileRef(resourceItem)) &&
72 ParseRefInJsonFile(resourceItem, output) != RESTOOL_SUCCESS) {
73 return RESTOOL_ERROR;
74 }
75 }
76 }
77 return RESTOOL_SUCCESS;
78 }
79
ParseRefInResourceItem(ResourceItem & resourceItem) const80 uint32_t ReferenceParser::ParseRefInResourceItem(ResourceItem &resourceItem) const
81 {
82 ResType resType = resourceItem.GetResType();
83 string data;
84 bool update = false;
85 if (IsStringOfResourceItem(resType)) {
86 data = string(reinterpret_cast<const char *>(resourceItem.GetData()), resourceItem.GetDataLength());
87 if (!ParseRefString(data, update)) {
88 cerr << "Error: " << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
89 return RESTOOL_ERROR;
90 }
91 if (!update) {
92 return RESTOOL_SUCCESS;
93 }
94 } else if (IsArrayOfResourceItem(resType)) {
95 if (!ParseRefResourceItemData(resourceItem, data, update)) {
96 return RESTOOL_ERROR;
97 }
98 if (!update) {
99 return RESTOOL_SUCCESS;
100 }
101 }
102 if (update && !resourceItem.SetData(reinterpret_cast<const int8_t *>(data.c_str()), data.length())) {
103 cerr << "Error: set data fail. name = '" << resourceItem.GetName() << "' data = '" << data << "'.";
104 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
105 return RESTOOL_ERROR;
106 }
107 return RESTOOL_SUCCESS;
108 }
109
ParseRefInJsonFile(ResourceItem & resourceItem,const string & output,const bool isIncrement)110 uint32_t ReferenceParser::ParseRefInJsonFile(ResourceItem &resourceItem, const string &output, const bool isIncrement)
111 {
112 string jsonPath;
113 if (resourceItem.GetResType() == ResType::MEDIA) {
114 jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR)
115 .Append(resourceItem.GetLimitKey()).Append("media").Append(resourceItem.GetName()).GetPath();
116 } else {
117 jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR)
118 .Append("base").Append("profile").Append(resourceItem.GetName()).GetPath();
119 }
120 if (!ParseRefJson(resourceItem.GetFilePath(), jsonPath)) {
121 return RESTOOL_ERROR;
122 }
123
124 if (isIncrement && ResourceUtil::FileExist(jsonPath)) {
125 resourceItem.SetData(reinterpret_cast<const int8_t *>(jsonPath.c_str()), jsonPath.length());
126 }
127 return RESTOOL_SUCCESS;
128 }
129
ParseRefInString(string & value,bool & update) const130 uint32_t ReferenceParser::ParseRefInString(string &value, bool &update) const
131 {
132 if (ParseRefString(value, update)) {
133 return RESTOOL_SUCCESS;
134 }
135 return RESTOOL_ERROR;
136 }
137
ParseRefJson(const string & from,const string & to) const138 bool ReferenceParser::ParseRefJson(const string &from, const string &to) const
139 {
140 Json::Value root;
141 if (!ResourceUtil::OpenJsonFile(from, root)) {
142 return false;
143 }
144
145 bool needSave = false;
146 if (!ParseRefJsonImpl(root, needSave)) {
147 return false;
148 }
149
150 if (!needSave) {
151 return true;
152 }
153
154 if (!ResourceUtil::CreateDirs(FileEntry::FilePath(to).GetParent().GetPath())) {
155 return false;
156 }
157
158 if (!ResourceUtil::SaveToJsonFile(to, root)) {
159 return false;
160 }
161 return true;
162 }
163
ParseRefResourceItemData(const ResourceItem & resourceItem,string & data,bool & update) const164 bool ReferenceParser::ParseRefResourceItemData(const ResourceItem &resourceItem, string &data, bool &update) const
165 {
166 data = string(reinterpret_cast<const char *>(resourceItem.GetData()), resourceItem.GetDataLength());
167 vector<string> contents = ResourceUtil::DecomposeStrings(data);
168 if (contents.empty()) {
169 cerr << "Error: DecomposeStrings fail. name = '" << resourceItem.GetName() << "' data = '" << data << "'.";
170 cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
171 return false;
172 }
173
174 for (auto &content : contents) {
175 bool flag = false;
176 if (!ParseRefString(content, flag)) {
177 cerr << "Error: " << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
178 return false;
179 }
180 update = (update || flag);
181 }
182
183 if (!update) {
184 return true;
185 }
186
187 data = ResourceUtil::ComposeStrings(contents);
188 if (data.empty()) {
189 cerr << "Error: ComposeStrings fail. name = '" << resourceItem.GetName();
190 cerr << "' contents size is " << contents.size() << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
191 return false;
192 }
193 return true;
194 }
195
IsStringOfResourceItem(ResType resType) const196 bool ReferenceParser::IsStringOfResourceItem(ResType resType) const
197 {
198 if (resType == ResType::STRING ||
199 resType == ResType::INTEGER ||
200 resType == ResType::BOOLEAN ||
201 resType == ResType::COLOR ||
202 resType == ResType::FLOAT ||
203 resType == ResType::SYMBOL) {
204 return true;
205 }
206 return false;
207 }
208
IsArrayOfResourceItem(ResType resType) const209 bool ReferenceParser::IsArrayOfResourceItem(ResType resType) const
210 {
211 if (resType == ResType::STRARRAY ||
212 resType == ResType::INTARRAY ||
213 resType == ResType::PLURAL ||
214 resType == ResType::THEME ||
215 resType == ResType::PATTERN) {
216 return true;
217 }
218 return false;
219 }
220
IsElementRef(const ResourceItem & resourceItem) const221 bool ReferenceParser::IsElementRef(const ResourceItem &resourceItem) const
222 {
223 ResType resType = resourceItem.GetResType();
224 auto result = find_if(g_contentClusterMap.begin(), g_contentClusterMap.end(), [resType](const auto &iter) {
225 return resType == iter.second;
226 });
227 if (result == g_contentClusterMap.end()) {
228 return false;
229 }
230 return true;
231 }
232
IsMediaRef(const ResourceItem & resourceItem) const233 bool ReferenceParser::IsMediaRef(const ResourceItem &resourceItem) const
234 {
235 return resourceItem.GetResType() == ResType::MEDIA &&
236 FileEntry::FilePath(resourceItem.GetFilePath()).GetExtension() == JSON_EXTENSION;
237 }
238
IsProfileRef(const ResourceItem & resourceItem) const239 bool ReferenceParser::IsProfileRef(const ResourceItem &resourceItem) const
240 {
241 return resourceItem.GetResType() == ResType::PROF && resourceItem.GetLimitKey() == "base" &&
242 FileEntry::FilePath(resourceItem.GetFilePath()).GetExtension() == JSON_EXTENSION;
243 }
244
ParseRefString(string & key) const245 bool ReferenceParser::ParseRefString(string &key) const
246 {
247 bool update = false;
248 return ParseRefString(key, update);
249 }
250
ParseRefString(std::string & key,bool & update) const251 bool ReferenceParser::ParseRefString(std::string &key, bool &update) const
252 {
253 update = false;
254 if (regex_match(key, regex("^\\$ohos:[a-z]+:.+"))) {
255 update = true;
256 return ParseRefImpl(key, ID_OHOS_REFS, true);
257 } else if (regex_match(key, regex("^\\$[a-z]+:.+"))) {
258 update = true;
259 return ParseRefImpl(key, ID_REFS, false);
260 }
261 return true;
262 }
263
ParseRefImpl(string & key,const map<string,ResType> & refs,bool isSystem) const264 bool ReferenceParser::ParseRefImpl(string &key, const map<string, ResType> &refs, bool isSystem) const
265 {
266 for (const auto &ref : refs) {
267 smatch result;
268 if (regex_search(key, result, regex(ref.first))) {
269 string name = key.substr(result[0].str().length());
270 int32_t id = idWorker_.GetId(ref.second, name);
271 if (isSystem) {
272 id = idWorker_.GetSystemId(ref.second, name);
273 }
274 if (id < 0) {
275 cerr << "Error: ref '" << key << "' don't be defined." << endl;
276 return false;
277 }
278
279 key = to_string(id);
280 if (ref.second != ResType::ID) {
281 key = "$" + ResourceUtil::ResTypeToString(ref.second) + ":" + to_string(id);
282 }
283 return true;
284 }
285 }
286 cerr << "Error: reference '" << key << "' invalid." << endl;
287 return false;
288 }
289
ParseRefJsonImpl(Json::Value & node,bool & needSave) const290 bool ReferenceParser::ParseRefJsonImpl(Json::Value &node, bool &needSave) const
291 {
292 if (node.isObject()) {
293 for (const auto &member : node.getMemberNames()) {
294 if (!ParseRefJsonImpl(node[member], needSave)) {
295 return false;
296 }
297 }
298 } else if (node.isArray()) {
299 for (Json::ArrayIndex i = 0; i < node.size(); i++) {
300 if (!ParseRefJsonImpl(node[i], needSave)) {
301 return false;
302 }
303 }
304 } else if (node.isString()) {
305 string value = node.asString();
306 bool update = false;
307 if (!ParseRefString(value, update)) {
308 return false;
309 }
310 if (update) {
311 needSave = update;
312 }
313 node = value;
314 }
315 return true;
316 }
317 }
318 }
319 }
320