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