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 std::string type = reinterpret_cast<const char*>(xmlNodeGetContent(value));
161 if (type == "True") {
162 flag = true;
163 }
164 value = value->next;
165 }
166 icu::UnicodeString content;
167 while (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("content"))) {
168 icu::UnicodeString tempContent = reinterpret_cast<char*>(xmlNodeGetContent(value));
169 content += tempContent;
170 value = value->next;
171 }
172 UErrorCode status = U_ZERO_ERROR;
173 icu::RegexPattern* pattern;
174 if (flag) {
175 pattern = icu::RegexPattern::compile(content, URegexpFlag::UREGEX_CASE_INSENSITIVE, status);
176 } else {
177 pattern = icu::RegexPattern::compile(content, 0, status);
178 }
179 map[key] = pattern;
180 cur = cur->next;
181 }
182 }
183
184 // load the rules of the cur node to the map
LoadStrToStr(std::unordered_map<std::string,std::string> * map,xmlNodePtr cur)185 void DateTimeRule::LoadStrToStr(std::unordered_map<std::string, std::string>* map, xmlNodePtr cur)
186 {
187 while (cur != nullptr) {
188 std::string key = reinterpret_cast<const char*>(cur->name);
189 // remove the first 3 chars of key.
190 key = key.substr(3);
191 xmlNodePtr value = cur->xmlChildrenNode;
192 // load rule level
193 if (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("level"))) {
194 std::string levelStr = reinterpret_cast<const char*>(xmlNodeGetContent(value));
195 int32_t status = 0;
196 int level = ConvertString2Int(levelStr, status);
197 levels[key] = level;
198 value = value->next;
199 }
200 // load rule content
201 std::string content;
202 while (value != nullptr && !xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>("content"))) {
203 std::string tempContent = reinterpret_cast<const char*>(xmlNodeGetContent(value));
204 content += tempContent;
205 value = value->next;
206 }
207 (*map)[key] = content;
208 cur = cur->next;
209 }
210 }
211
212 // 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)213 std::string DateTimeRule::Get(std::unordered_map<std::string, std::string>& paramMap, std::string& ruleName)
214 {
215 std::string result = "";
216 if (paramMap.empty() || paramMap.find(ruleName) == paramMap.end()) {
217 return result;
218 }
219 result = paramMap[ruleName];
220 std::vector<std::string> temps;
221 std::string splitStr = "|";
222 Split(result, splitStr, temps);
223 std::string sb;
224 std::string mark = "";
225 if (delimiter.find(locale) != delimiter.end()) {
226 mark = delimiter[locale];
227 } else {
228 mark = "\\b";
229 }
230 for (auto& temp : temps) {
231 if (!CompareBeginEnd(temp, "\\b", true)) {
232 sb += mark;
233 }
234 sb += temp;
235 if (!CompareBeginEnd(temp, "\\b", false) && !CompareBeginEnd(temp, ".", false)) {
236 sb += mark;
237 }
238 sb += "|";
239 }
240 result = sb;
241 if (CompareBeginEnd(result, "|", false)) {
242 result.pop_back();
243 }
244 return result;
245 }
246
247 // check whether src starts or ends with target.
CompareBeginEnd(const std::string src,const std::string target,bool flag)248 bool DateTimeRule::CompareBeginEnd(const std::string src, const std::string target, bool flag)
249 {
250 int lengthSrc = src.length();
251 int lengthTarget = target.length();
252 if (lengthSrc < lengthTarget) {
253 return false;
254 }
255 std::string subStr;
256 if (flag) {
257 subStr = src.substr(0, lengthTarget);
258 } else {
259 subStr = src.substr(lengthSrc - lengthTarget, lengthTarget);
260 }
261 return subStr == target;
262 }
263
264 // replace subStr in src with newStr.
SubReplace(std::string & src,const std::string & subStr,const std::string & newStr)265 std::string DateTimeRule::SubReplace(std::string& src, const std::string& subStr, const std::string& newStr)
266 {
267 std::string::size_type pos = 0;
268 std::string result = src;
269 if (subStr.empty() || subStr.compare(newStr) == 0) {
270 return result;
271 }
272 while ((pos = result.find(subStr)) != std::string::npos) {
273 result.replace(pos, subStr.length(), newStr);
274 }
275 return result;
276 }
277
GetWithoutB(const std::string & ruleName)278 std::string DateTimeRule::GetWithoutB(const std::string& ruleName)
279 {
280 std::string result = "";
281 if (param.empty() || param.find(ruleName) == param.end()) {
282 return result;
283 }
284 result = param[ruleName];
285 return result;
286 }
287
288 // check whether hyphen is a valid date and time separator.
IsRelDates(icu::UnicodeString & hyphen,std::string & locale)289 bool DateTimeRule::IsRelDates(icu::UnicodeString& hyphen, std::string& locale)
290 {
291 bool isRel = false;
292 if (hyphen.trim().isEmpty()) {
293 isRel = true;
294 } else if (hyphen.trim() == ',' && relDates.find(locale) != relDates.end() &&
295 relDates[locale].find(",") != std::string::npos) {
296 isRel = true;
297 }
298 return isRel;
299 }
300
trim(const std::string & src)301 std::string DateTimeRule::trim(const std::string& src)
302 {
303 std::string target = src;
304 if (target.empty()) {
305 return target;
306 }
307 target.erase(0, target.find_first_not_of(" "));
308 target.erase(target.find_last_not_of(" ") + 1);
309 return target;
310 }
311
CompareLevel(std::string & key1,std::string & key2)312 int DateTimeRule::CompareLevel(std::string& key1, std::string& key2)
313 {
314 int result = 0;
315 int level1 = GetLevel(key1);
316 int level2 = GetLevel(key2);
317 if (level1 > level2) {
318 result = 1;
319 } else if (level1 < level2) {
320 result = -1;
321 }
322 return result;
323 }
324
GetLevel(std::string & name)325 int DateTimeRule::GetLevel(std::string& name)
326 {
327 int baselevel;
328 int32_t status = 0;
329 int key = ConvertString2Int(name, status);
330 // 9999 and 20000 are the rule numbers.
331 if (key > 9999 && key < 20000) {
332 // 10 is basic level.
333 baselevel = 10;
334 // 19999 and 40000 are the rule numbers.
335 } else if (key > 19999 && key < 40000) {
336 // 20 is basic level.
337 baselevel = 20;
338 } else {
339 // 30 is basic level.
340 baselevel = 30;
341 }
342 int addLeve = 1;
343 if (levels.find(name) != levels.end()) {
344 addLeve = levels[name];
345 }
346 int level = baselevel + addLeve;
347 return level;
348 }
349
GetUniverseRules()350 std::unordered_map<std::string, std::string> DateTimeRule::GetUniverseRules()
351 {
352 return universeRules;
353 }
354
GetLocalesRules()355 std::unordered_map<std::string, std::string> DateTimeRule::GetLocalesRules()
356 {
357 return localesRules;
358 }
359
GetLocalesRulesBackup()360 std::unordered_map<std::string, std::string> DateTimeRule::GetLocalesRulesBackup()
361 {
362 return localesRulesBackup;
363 }
364
GetSubRulesMap()365 std::unordered_map<std::string, std::unordered_map<std::string, std::string>> DateTimeRule::GetSubRulesMap()
366 {
367 return subRulesMap;
368 }
369
GetSubRules()370 std::unordered_map<std::string, std::string> DateTimeRule::GetSubRules()
371 {
372 return subRules;
373 }
374
GetFilterRules()375 std::unordered_map<std::string, std::string> DateTimeRule::GetFilterRules()
376 {
377 return filterRules;
378 }
379
GetPastRules()380 std::unordered_map<std::string, std::string> DateTimeRule::GetPastRules()
381 {
382 return pastRules;
383 }
384
GetParam()385 std::unordered_map<std::string, std::string> DateTimeRule::GetParam()
386 {
387 return param;
388 }
389
GetParamBackup()390 std::unordered_map<std::string, std::string> DateTimeRule::GetParamBackup()
391 {
392 return paramBackup;
393 }
394
GetPatternsMap()395 std::unordered_map<std::string, icu::RegexPattern*> DateTimeRule::GetPatternsMap()
396 {
397 return patternsMap;
398 }
399 } // namespace I18n
400 } // namespace Global
401 } // namespace OHOS