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