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(¶mBackup, 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