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