1 /*
2  * Copyright (c) 2021-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 "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 
56 std::map<int64_t, std::set<int64_t>> ReferenceParser::layerIconIds_;
57 
ReferenceParser()58 ReferenceParser::ReferenceParser() : idWorker_(IdWorker::GetInstance()), root_(nullptr), isParsingMediaJson_(false)
59 {
60 }
61 
~ReferenceParser()62 ReferenceParser::~ReferenceParser()
63 {
64     if (root_) {
65         cJSON_Delete(root_);
66     }
67 }
68 
ParseRefInResources(map<int64_t, vector<ResourceItem>> &items, const string &output)69 uint32_t ReferenceParser::ParseRefInResources(map<int64_t, vector<ResourceItem>> &items, const string &output)
70 {
71     for (auto &iter : items) {
72         for (auto &resourceItem : iter.second) {
73             if (IsElementRef(resourceItem) && ParseRefInResourceItem(resourceItem) != RESTOOL_SUCCESS) {
74                 return RESTOOL_ERROR;
75             }
76             if ((IsMediaRef(resourceItem) || IsProfileRef(resourceItem)) &&
77                 ParseRefInJsonFile(resourceItem, output) != RESTOOL_SUCCESS) {
78                 return RESTOOL_ERROR;
79             }
80         }
81     }
82     return RESTOOL_SUCCESS;
83 }
84 
ParseRefInResourceItem(ResourceItem &resourceItem) const85 uint32_t ReferenceParser::ParseRefInResourceItem(ResourceItem &resourceItem) const
86 {
87     ResType resType = resourceItem.GetResType();
88     string data;
89     bool update = false;
90     if (IsStringOfResourceItem(resType)) {
91         data = string(reinterpret_cast<const char *>(resourceItem.GetData()), resourceItem.GetDataLength());
92         if (!ParseRefString(data, update)) {
93             cerr << "Error: please check JSON file." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
94             return RESTOOL_ERROR;
95         }
96         if (!update) {
97             return RESTOOL_SUCCESS;
98         }
99     } else if (IsArrayOfResourceItem(resType)) {
100         if (!ParseRefResourceItemData(resourceItem, data, update)) {
101             return RESTOOL_ERROR;
102         }
103         if (!update) {
104             return RESTOOL_SUCCESS;
105         }
106     }
107     if (update && !resourceItem.SetData(reinterpret_cast<const int8_t *>(data.c_str()), data.length())) {
108         cerr << "Error: set data fail. name = '" << resourceItem.GetName() << "' data = '" << data << "'.";
109         cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
110         return RESTOOL_ERROR;
111     }
112     return RESTOOL_SUCCESS;
113 }
114 
ParseRefInJsonFile(ResourceItem &resourceItem, const string &output, const bool isIncrement)115 uint32_t ReferenceParser::ParseRefInJsonFile(ResourceItem &resourceItem, const string &output, const bool isIncrement)
116 {
117     string jsonPath;
118     ResType resType = resourceItem.GetResType();
119     string resName = resourceItem.GetName();
120     if (resType == ResType::MEDIA) {
121         jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append(resourceItem.GetLimitKey()).Append("media")
122             .Append(resName).GetPath();
123         isParsingMediaJson_ = true;
124         mediaJsonId_ = idWorker_.GetId(resType, ResourceUtil::GetIdName(resName, resType));
125         if (mediaJsonId_ != INVALID_ID) {
126             set<int64_t> set;
127             layerIconIds_[mediaJsonId_] = set;
128         }
129     } else {
130         jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append("base").Append("profile").Append(resName)
131             .GetPath();
132     }
133     bool parseJsonRet = ParseRefJson(resourceItem.GetFilePath(), jsonPath);
134     isParsingMediaJson_ = false;
135     mediaJsonId_ = INVALID_ID;
136     if (!parseJsonRet) {
137         return RESTOOL_ERROR;
138     }
139 
140     if (isIncrement && ResourceUtil::FileExist(jsonPath)) {
141         resourceItem.SetData(reinterpret_cast<const int8_t *>(jsonPath.c_str()), jsonPath.length());
142     }
143     return RESTOOL_SUCCESS;
144 }
145 
ParseRefInString(string &value, bool &update) const146 uint32_t ReferenceParser::ParseRefInString(string &value, bool &update) const
147 {
148     if (ParseRefString(value, update)) {
149         return RESTOOL_SUCCESS;
150     }
151     return RESTOOL_ERROR;
152 }
153 
ParseRefJson(const string &from, const string &to)154 bool ReferenceParser::ParseRefJson(const string &from, const string &to)
155 {
156     if (!ResourceUtil::OpenJsonFile(from, &root_)) {
157         return false;
158     }
159     if (!root_ || !cJSON_IsObject(root_)) {
160         cerr << "Error: JSON file parsing failed, please check the JSON file." << NEW_LINE_PATH << from << endl;
161         return RESTOOL_ERROR;
162     }
163     bool needSave = false;
164     if (!ParseRefJsonImpl(root_, needSave)) {
165         cerr << "Error: please check JSON file." << NEW_LINE_PATH << from << endl;
166         return false;
167     }
168 
169     if (!needSave) {
170         return true;
171     }
172 
173     if (!ResourceUtil::CreateDirs(FileEntry::FilePath(to).GetParent().GetPath())) {
174         return false;
175     }
176 
177     if (!ResourceUtil::SaveToJsonFile(to, root_)) {
178         return false;
179     }
180     return true;
181 }
182 
ParseRefResourceItemData(const ResourceItem &resourceItem, string &data, bool &update) const183 bool ReferenceParser::ParseRefResourceItemData(const ResourceItem &resourceItem, string &data, bool &update) const
184 {
185     data = string(reinterpret_cast<const char *>(resourceItem.GetData()), resourceItem.GetDataLength());
186     vector<string> contents = ResourceUtil::DecomposeStrings(data);
187     if (contents.empty()) {
188         cerr << "Error: DecomposeStrings fail. name = '" << resourceItem.GetName() << "' data = '" << data << "'.";
189         cerr << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
190         return false;
191     }
192 
193     for (auto &content : contents) {
194         bool flag = false;
195         if (!ParseRefString(content, flag)) {
196             cerr << "Error: please check JSON file." << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
197             return false;
198         }
199         update = (update || flag);
200     }
201 
202     if (!update) {
203         return true;
204     }
205 
206     data = ResourceUtil::ComposeStrings(contents);
207     if (data.empty()) {
208         cerr << "Error: ComposeStrings fail. name = '" << resourceItem.GetName();
209         cerr << "'  contents size is " << contents.size() << NEW_LINE_PATH << resourceItem.GetFilePath() << endl;
210         return false;
211     }
212     return true;
213 }
214 
IsStringOfResourceItem(ResType resType) const215 bool ReferenceParser::IsStringOfResourceItem(ResType resType) const
216 {
217     if (resType == ResType::STRING ||
218         resType == ResType::INTEGER ||
219         resType == ResType::BOOLEAN ||
220         resType == ResType::COLOR ||
221         resType == ResType::FLOAT ||
222         resType == ResType::SYMBOL) {
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 
IsElementRef(const ResourceItem &resourceItem) const240 bool ReferenceParser::IsElementRef(const ResourceItem &resourceItem) const
241 {
242     ResType resType = resourceItem.GetResType();
243     auto result = find_if(g_contentClusterMap.begin(), g_contentClusterMap.end(), [resType](const auto &iter) {
244         return resType == iter.second;
245     });
246     if (result == g_contentClusterMap.end()) {
247         return false;
248     }
249     return true;
250 }
251 
IsMediaRef(const ResourceItem &resourceItem) const252 bool ReferenceParser::IsMediaRef(const ResourceItem &resourceItem) const
253 {
254     return resourceItem.GetResType() == ResType::MEDIA &&
255                 FileEntry::FilePath(resourceItem.GetFilePath()).GetExtension() == JSON_EXTENSION;
256 }
257 
IsProfileRef(const ResourceItem &resourceItem) const258 bool ReferenceParser::IsProfileRef(const ResourceItem &resourceItem) const
259 {
260     return resourceItem.GetResType() == ResType::PROF && resourceItem.GetLimitKey() == "base" &&
261                 FileEntry::FilePath(resourceItem.GetFilePath()).GetExtension() == JSON_EXTENSION;
262 }
263 
ParseRefString(string &key) const264 bool ReferenceParser::ParseRefString(string &key) const
265 {
266     bool update = false;
267     return ParseRefString(key, update);
268 }
269 
ParseRefString(std::string &key, bool &update) const270 bool ReferenceParser::ParseRefString(std::string &key, bool &update) const
271 {
272     update = false;
273     if (regex_match(key, regex("^\\$ohos:[a-z]+:.+"))) {
274         update = true;
275         return ParseRefImpl(key, ID_OHOS_REFS, true);
276     } else if (regex_match(key, regex("^\\$[a-z]+:.+"))) {
277         update = true;
278         return ParseRefImpl(key, ID_REFS, false);
279     }
280     return true;
281 }
282 
ParseRefImpl(string &key, const map<string, ResType> &refs, bool isSystem) const283 bool ReferenceParser::ParseRefImpl(string &key, const map<string, ResType> &refs, bool isSystem) const
284 {
285     for (const auto &ref : refs) {
286         smatch result;
287         if (regex_search(key, result, regex(ref.first))) {
288             string name = key.substr(result[0].str().length());
289             int64_t id = idWorker_.GetId(ref.second, name);
290             if (!isSystem && ref.second == ResType::MEDIA && mediaJsonId_ != 0
291                 && layerIconIds_.find(mediaJsonId_) != layerIconIds_.end()) {
292                 layerIconIds_[mediaJsonId_].insert(id);
293             }
294             if (isSystem) {
295                 id = idWorker_.GetSystemId(ref.second, name);
296             }
297             if (id < 0) {
298                 cerr << "Error: ref '" << key << "' don't be defined." << endl;
299                 return false;
300             }
301 
302             key = to_string(id);
303             if (ref.second != ResType::ID) {
304                 key = "$" + ResourceUtil::ResTypeToString(ref.second) + ":" + to_string(id);
305             }
306             return true;
307         }
308     }
309     cerr << "Error: reference '" << key << "' invalid." << endl;
310     return false;
311 }
312 
ParseRefJsonImpl(cJSON *node, bool &needSave) const313 bool ReferenceParser::ParseRefJsonImpl(cJSON *node, bool &needSave) const
314 {
315     if (cJSON_IsObject(node) || cJSON_IsArray(node)) {
316         for (cJSON *item = node->child; item; item = item->next) {
317             if (!ParseRefJsonImpl(item, needSave)) {
318                 return false;
319             }
320         }
321     }  else if (cJSON_IsString(node)) {
322         string value = node->valuestring;
323         bool update = false;
324         if (!ParseRefString(value, update)) {
325             return false;
326         }
327         if (update) {
328             needSave = update;
329         }
330         cJSON_SetValuestring(node, value.c_str());
331     }
332     return true;
333 }
334 
GetLayerIconIds()335 std::map<int64_t, std::set<int64_t>> &ReferenceParser::GetLayerIconIds()
336 {
337     return layerIconIds_;
338 }
339 }
340 }
341 }
342