1 /*
2  * Copyright (c) 2023 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 locale governing permissions and
13  * limitations under the License.
14  */
15 #include "date_time_rule.h"
16 #include "utils.h"
17 
18 namespace OHOS {
19 namespace Global {
20 namespace I18n {
21 std::string DateTimeRule::xmlCommonPath = "/system/usr/ohos_locale_config/datetime/common.xml";
22 
DateTimeRule(std::string & locale)23 DateTimeRule::DateTimeRule(std::string& locale)
24 {
25     this->loadMap = {{"sub_rules", &this->subRules},
26                      {"universe_rules", &this->universeRules},
27                      {"filter_rules", &this->filterRules},
28                      {"past_rules", &this->pastRules},
29                      {"locale_rules", &this->localesRules},
30                      {"delimiter", &this->delimiter},
31                      {"param", &this->param},
32                      {"default_locale", &this->localeMap},
33                      {"isRelDates", &this->relDates}};
34     Init(locale);
35 }
36 
~DateTimeRule()37 DateTimeRule::~DateTimeRule()
38 {
39     std::unordered_map<std::string, icu::RegexPattern*>::iterator iter;
40     for (iter = patternsMap.begin(); iter != patternsMap.end(); ++iter) {
41         icu::RegexPattern* pattern = iter->second;
42         if (pattern != nullptr) {
43             delete pattern;
44         }
45         pattern = nullptr;
46     }
47 }
48 
Init(std::string & locale)49 void DateTimeRule::Init(std::string& locale)
50 {
51     InitRules(xmlCommonPath);
52     std::string xmlPath = "/system/usr/ohos_locale_config/datetime/" + locale + ".xml";
53     if (!CheckTzDataFilePath(xmlPath)) {
54         this->locale = this->localeMap["locale"];
55     } else {
56         this->locale = locale;
57     }
58     xmlPath = "/system/usr/ohos_locale_config/datetime/" + this->locale + ".xml";
59     InitRules(xmlPath);
60     std::string xmlPathBackup = "/system/usr/ohos_locale_config/datetime/" + this->localeMap["backup"] + ".xml";
61     InitRuleBackup(xmlPathBackup);
62     RuleLevel();
63 }
64 
GetLocale()65 std::string DateTimeRule::GetLocale()
66 {
67     return this->locale;
68 }
69 
RuleLevel()70 void DateTimeRule::RuleLevel()
71 {
72     std::string ruleName = "mark_ShortDateLevel";
73     std::string shortDateMark = GetWithoutB(ruleName);
74     if (shortDateMark == "ymd") {
75         // 1 indicates the level of the "20016" rule in the "ymd" style.
76         levels["20016"] = 1;
77         // 3 indicates the level of the "20014" rule in the "ymd" style.
78         levels["20014"] = 3;
79         // 2 indicates the level of the "20015" rule in the "ymd" style.
80         levels["20015"] = 2;
81     } else if (shortDateMark == "mdy") {
82         // 2 indicates the level of the "20016" rule in the "mdy" style.
83         levels["20016"] = 2;
84         // 3 indicates the level of the "20014" rule in the "mdy" style.
85         levels["20014"] = 3;
86         // 1 indicates the level of the "20015" rule in the "mdy" style.
87         levels["20015"] = 1;
88     }
89 }
90 
InitRules(std::string & xmlPath)91 void DateTimeRule::InitRules(std::string& xmlPath)
92 {
93     if (!CheckTzDataFilePath(xmlPath)) {
94         return;
95     }
96     xmlKeepBlanksDefault(0);
97     xmlDocPtr doc = xmlParseFile(xmlPath.c_str());
98     if (doc == nullptr) return;
99     xmlNodePtr root = xmlDocGetRootElement(doc);
100     xmlNodePtr cur = root->xmlChildrenNode;
101     while (cur != nullptr) {
102         std::string category = reinterpret_cast<const char*>(cur->name);
103         if (category == "sub_rules_map") {
104             xmlNodePtr value = cur->xmlChildrenNode;
105             while (value != nullptr) {
106                 std::string key = reinterpret_cast<const char*>(value->name);
107                 // remove the first 3 chars of key.
108                 key = key.substr(3);
109                 xmlNodePtr subValue = value->xmlChildrenNode;
110                 std::unordered_map<std::string, std::string> tempMap;
111                 LoadStrToStr(&tempMap, subValue);
112                 subRulesMap[key] = tempMap;
113                 value = value->next;
114             }
115         } else if (this->loadMap.find(category) != this->loadMap.end()) {
116             xmlNodePtr valueNext = cur->xmlChildrenNode;
117             LoadStrToStr(this->loadMap[category], valueNext);
118         } else if (category == "pattern") {
119             xmlNodePtr valueNext = cur->xmlChildrenNode;
120             LoadStrToPattern(this->patternsMap, valueNext);
121         }
122         cur = cur->next;
123     }
124     xmlFreeDoc(doc);
125 }
126 
InitRuleBackup(std::string & xmlPathBackup)127 void DateTimeRule::InitRuleBackup(std::string& xmlPathBackup)
128 {
129     if (!CheckTzDataFilePath(xmlPathBackup)) {
130         return;
131     }
132     xmlKeepBlanksDefault(0);
133     xmlDocPtr doc = xmlParseFile(xmlPathBackup.c_str());
134     if (doc == nullptr) return;
135     xmlNodePtr cur = xmlDocGetRootElement(doc)->xmlChildrenNode;
136     while (cur != nullptr) {
137         std::string category = reinterpret_cast<const char*>(cur->name);
138         xmlNodePtr valueNext = cur->xmlChildrenNode;
139         if (category == "param") {
140             LoadStrToStr(&paramBackup, valueNext);
141         } else if (category == "locale_rules") {
142             LoadStrToStr(&localesRulesBackup, valueNext);
143         }
144         cur = cur->next;
145     }
146     xmlFreeDoc(doc);
147 }
148 
149 // load the rules of the cur node to the map
LoadStrToPattern(std::unordered_map<std::string,icu::RegexPattern * > & map,xmlNodePtr cur)150 void DateTimeRule::LoadStrToPattern(std::unordered_map<std::string, icu::RegexPattern*>& map, xmlNodePtr cur)
151 {
152     while (cur != nullptr) {
153         std::string key = reinterpret_cast<const char*>(cur->name);
154         // remove the first 3 chars of key.
155         key = key.substr(3);
156         xmlNodePtr value = cur->xmlChildrenNode;
157         bool flag = false;
158         // load rule content
159         if (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("flag"))) {
160             xmlChar* typePtr = xmlNodeGetContent(value);
161             if (typePtr != nullptr) {
162                 std::string type = reinterpret_cast<const char*>(typePtr);
163                 flag = (type == "True") ? true : flag;
164                 xmlFree(typePtr);
165             }
166             value = value->next;
167         }
168         icu::UnicodeString content;
169         while (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("content"))) {
170             xmlChar* contentPtr = xmlNodeGetContent(value);
171             if (contentPtr != nullptr) {
172                 icu::UnicodeString tempContent = reinterpret_cast<char*>(contentPtr);
173                 content += tempContent;
174                 xmlFree(contentPtr);
175             }
176             value = value->next;
177         }
178         UErrorCode status = U_ZERO_ERROR;
179         icu::RegexPattern* pattern;
180         if (flag) {
181             pattern = icu::RegexPattern::compile(content, URegexpFlag::UREGEX_CASE_INSENSITIVE, status);
182         } else {
183             pattern = icu::RegexPattern::compile(content, 0, status);
184         }
185         map[key] = pattern;
186         cur = cur->next;
187     }
188 }
189 
190 // load the rules of the cur node to the map
LoadStrToStr(std::unordered_map<std::string,std::string> * map,xmlNodePtr cur)191 void DateTimeRule::LoadStrToStr(std::unordered_map<std::string, std::string>* map, xmlNodePtr cur)
192 {
193     while (cur != nullptr) {
194         std::string key = reinterpret_cast<const char*>(cur->name);
195         // remove the first 3 chars of key.
196         key = key.substr(3);
197         xmlNodePtr value = cur->xmlChildrenNode;
198         // load rule level
199         if (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("level"))) {
200             xmlChar* levelPtr = xmlNodeGetContent(value);
201             if (levelPtr != nullptr) {
202                 std::string levelStr = reinterpret_cast<const char*>(levelPtr);
203                 int32_t status = 0;
204                 int level = ConvertString2Int(levelStr, status);
205                 levels[key] = level;
206                 xmlFree(levelPtr);
207             } else {
208                 break;
209             }
210             value = value->next;
211         }
212         // load rule content
213         std::string content;
214         while (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("content"))) {
215             xmlChar* contentPtr = xmlNodeGetContent(value);
216             if (contentPtr != nullptr) {
217                 std::string tempContent = reinterpret_cast<const char*>(contentPtr);
218                 content += tempContent;
219                 xmlFree(contentPtr);
220             }
221             value = value->next;
222         }
223         (*map)[key] = content;
224         cur = cur->next;
225     }
226 }
227 
228 // process rule paramMap[ruleName]. for example: Sat|Mon|Tue  ->  \\bSat\\b|\\bMon\\b|\\bTue\\b
Get(std::unordered_map<std::string,std::string> & paramMap,std::string & ruleName)229 std::string DateTimeRule::Get(std::unordered_map<std::string, std::string>& paramMap, std::string& ruleName)
230 {
231     std::string result = "";
232     if (paramMap.empty() || paramMap.find(ruleName) == paramMap.end()) {
233         return result;
234     }
235     result = paramMap[ruleName];
236     std::vector<std::string> temps;
237     std::string splitStr = "|";
238     Split(result, splitStr, temps);
239     std::string sb;
240     std::string mark = "";
241     if (delimiter.find(locale) != delimiter.end()) {
242         mark = delimiter[locale];
243     } else {
244         mark = "\\b";
245     }
246     for (auto& temp : temps) {
247         if (!CompareBeginEnd(temp, "\\b", true)) {
248             sb += mark;
249         }
250         sb += temp;
251         if (!CompareBeginEnd(temp, "\\b", false) && !CompareBeginEnd(temp, ".", false)) {
252             sb += mark;
253         }
254         sb += "|";
255     }
256     result = sb;
257     if (CompareBeginEnd(result, "|", false)) {
258         result.pop_back();
259     }
260     return result;
261 }
262 
263 // check whether src starts or ends with target.
CompareBeginEnd(const std::string src,const std::string target,bool flag)264 bool DateTimeRule::CompareBeginEnd(const std::string src, const std::string target, bool flag)
265 {
266     size_t lengthSrc = src.length();
267     size_t lengthTarget = target.length();
268     if (lengthSrc < lengthTarget) {
269         return false;
270     }
271     std::string subStr;
272     if (flag) {
273         subStr = src.substr(0, lengthTarget);
274     } else {
275         subStr = src.substr(lengthSrc - lengthTarget, lengthTarget);
276     }
277     return subStr == target;
278 }
279 
280 
GetWithoutB(const std::string & ruleName)281 std::string DateTimeRule::GetWithoutB(const std::string& ruleName)
282 {
283     std::string result = "";
284     if (param.empty() || param.find(ruleName) == param.end()) {
285         return result;
286     }
287     result = param[ruleName];
288     return result;
289 }
290 
291 // check whether hyphen is a valid date and time separator.
IsRelDates(icu::UnicodeString & hyphen,std::string & locale)292 bool DateTimeRule::IsRelDates(icu::UnicodeString& hyphen, std::string& locale)
293 {
294     bool isRel = false;
295     if (hyphen.trim().isEmpty()) {
296         isRel = true;
297     } else if (hyphen.trim() == ',' && relDates.find(locale) != relDates.end() &&
298         relDates[locale].find(",") != std::string::npos) {
299         isRel = true;
300     }
301     return isRel;
302 }
303 
trim(const std::string & src)304 std::string DateTimeRule::trim(const std::string& src)
305 {
306     std::string target = src;
307     if (target.empty()) {
308         return target;
309     }
310     target.erase(0, target.find_first_not_of(" "));
311     target.erase(target.find_last_not_of(" ") + 1);
312     return target;
313 }
314 
CompareLevel(std::string & key1,std::string & key2)315 int DateTimeRule::CompareLevel(std::string& key1, std::string& key2)
316 {
317     int result = 0;
318     int level1 = GetLevel(key1);
319     int level2 = GetLevel(key2);
320     if (level1 > level2) {
321         result = 1;
322     } else if (level1 < level2) {
323         result = -1;
324     }
325     return result;
326 }
327 
GetLevel(std::string & name)328 int DateTimeRule::GetLevel(std::string& name)
329 {
330     int baselevel;
331     int32_t status = 0;
332     int key = ConvertString2Int(name, status);
333     // 9999 and 20000 are the rule numbers.
334     if (key > 9999 && key < 20000) {
335         // 10 is basic level.
336         baselevel = 10;
337     // 19999 and 40000 are the rule numbers.
338     } else if (key > 19999 && key < 40000) {
339         // 20 is basic level.
340         baselevel = 20;
341     } else {
342         // 30 is basic level.
343         baselevel = 30;
344     }
345     int addLeve = 1;
346     if (levels.find(name) != levels.end()) {
347         addLeve = levels[name];
348     }
349     int level = baselevel + addLeve;
350     return level;
351 }
352 
GetUniverseRules()353 std::unordered_map<std::string, std::string> DateTimeRule::GetUniverseRules()
354 {
355     return universeRules;
356 }
357 
GetLocalesRules()358 std::unordered_map<std::string, std::string> DateTimeRule::GetLocalesRules()
359 {
360     return localesRules;
361 }
362 
GetLocalesRulesBackup()363 std::unordered_map<std::string, std::string> DateTimeRule::GetLocalesRulesBackup()
364 {
365     return localesRulesBackup;
366 }
367 
GetSubRulesMap()368 std::unordered_map<std::string, std::unordered_map<std::string, std::string>> DateTimeRule::GetSubRulesMap()
369 {
370     return subRulesMap;
371 }
372 
GetSubRules()373 std::unordered_map<std::string, std::string> DateTimeRule::GetSubRules()
374 {
375     return subRules;
376 }
377 
GetFilterRules()378 std::unordered_map<std::string, std::string> DateTimeRule::GetFilterRules()
379 {
380     return filterRules;
381 }
382 
GetPastRules()383 std::unordered_map<std::string, std::string> DateTimeRule::GetPastRules()
384 {
385     return pastRules;
386 }
387 
GetParam()388 std::unordered_map<std::string, std::string> DateTimeRule::GetParam()
389 {
390     return param;
391 }
392 
GetParamBackup()393 std::unordered_map<std::string, std::string> DateTimeRule::GetParamBackup()
394 {
395     return paramBackup;
396 }
397 
GetPatternsMap()398 std::unordered_map<std::string, icu::RegexPattern*> DateTimeRule::GetPatternsMap()
399 {
400     return patternsMap;
401 }
402 } // namespace I18n
403 } // namespace Global
404 } // namespace OHOS