1 /*
2 * Copyright (c) 2021-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 language governing permissions and
13 * limitations under the License.
14 */
15 #include "locale_matcher.h"
16
17 #include <cstring>
18 #include <new>
19 #include "likely_subtags_key_data.cpp"
20 #include "likely_subtags_value_data.cpp"
21
22 #include "utils/locale_data.cpp"
23 #include "utils/common.h"
24 #include "utils/utils.h"
25
26 namespace OHOS {
27 namespace Global {
28 namespace Resource {
29 // Special locale constant
30 uint64_t LocaleMatcher::EN_US_ENCODE = Utils::EncodeLocale("en", nullptr, "US");
31 uint64_t LocaleMatcher::EN_GB_ENCODE = Utils::EncodeLocale("en", nullptr, "GB");
32 uint64_t LocaleMatcher::EN_QAAG_ENCODE = Utils::EncodeLocale("en", "Qaag", nullptr);
33 uint64_t LocaleMatcher::ZH_HANT_MO_ENCODE = Utils::EncodeLocale("zh", "Hant", "MO");
34 uint64_t LocaleMatcher::ZH_HK_ENCODE = Utils::EncodeLocale("zh", nullptr, "HK");
35 uint32_t LocaleMatcher::HANT_ENCODE = Utils::EncodeScript("Hant");
36
IsContainRegion(uint64_t encodedLocale)37 bool IsContainRegion(uint64_t encodedLocale)
38 {
39 return (encodedLocale & 0x000000000000FFFFLU) != 0;
40 }
41
ClearRegion(uint64_t encodedLocale)42 uint64_t ClearRegion(uint64_t encodedLocale)
43 {
44 return encodedLocale & 0xFFFFFFFFFFFF0000LU;
45 }
46
AddScript(uint64_t encodedLocale,uint32_t encodedScript)47 uint64_t AddScript(uint64_t encodedLocale, uint32_t encodedScript)
48 {
49 return (encodedLocale | ((static_cast<uint64_t>(encodedScript) & 0x00000000FFFFFFFFLU) <<
50 BitOperatorNum::BIT_SIXTEEN));
51 }
52
53 /**
54 * @brief Find region parent locale, if locale has not contain region, return ROOT_LOCALE.
55 * Lookup child-parent locale tables, if tables has not contains, then clear the locale region and return it.
56 *
57 * @param encodedLocale locale encode
58 * @return uint64_t parent locale encode
59 */
SearchParentLocale(uint64_t encodedLocale,const ResLocale * request)60 uint64_t SearchParentLocale(uint64_t encodedLocale, const ResLocale *request)
61 {
62 uint64_t tempEncodedLocale = encodedLocale;
63 if (Utils::EncodeScriptByResLocale(request) == LocaleMatcher::HANT_ENCODE) {
64 tempEncodedLocale = AddScript(encodedLocale, LocaleMatcher::HANT_ENCODE);
65 if (tempEncodedLocale == LocaleMatcher::ZH_HANT_MO_ENCODE) {
66 return LocaleMatcher::ZH_HK_ENCODE;
67 }
68 }
69 if (IsContainRegion(encodedLocale)) {
70 size_t len = sizeof(LOCALE_PARENTS_KEY) / sizeof(LOCALE_PARENTS_KEY[0]);
71 for (size_t i = 0; i < len; i++) {
72 if (LOCALE_PARENTS_KEY[i] == tempEncodedLocale) {
73 return LOCALE_PARENTS_VALUE[i];
74 }
75 }
76 return ClearRegion(encodedLocale);
77 }
78 return LocaleMatcher::ROOT_LOCALE;
79 }
80
81 /**
82 * @brief find locale ancestors, including itself and ROOT_LOCALE.
83 *
84 * @param result
85 * @param len
86 * @param encodedLocale
87 */
FindTrackPath(const ResLocale * request,size_t len,uint64_t encodedLocale,uint64_t * result)88 void FindTrackPath(const ResLocale *request, size_t len, uint64_t encodedLocale, uint64_t *result)
89 {
90 uint64_t currentEncodedLocale = encodedLocale;
91 size_t i = 0;
92 do {
93 result[i] = currentEncodedLocale;
94 currentEncodedLocale = SearchParentLocale(currentEncodedLocale, request);
95 ++i;
96 } while (currentEncodedLocale != LocaleMatcher::ROOT_LOCALE);
97 if (i < len) {
98 result[i] = LocaleMatcher::ROOT_LOCALE;
99 }
100 }
101
102 /**
103 * @brief find encodedLocale pos is in trackpath list.
104 *
105 * @param paths
106 * @param len
107 * @param encodedLocale
108 * @return int8_t
109 */
SearchTrackPathDistance(const uint64_t * paths,size_t len,uint64_t encodedLocale)110 int8_t SearchTrackPathDistance(const uint64_t *paths, size_t len, uint64_t encodedLocale)
111 {
112 size_t i = 0;
113 for (i = 0; i < len; ++i) {
114 if (paths[i] == LocaleMatcher::ROOT_LOCALE) {
115 return i;
116 }
117 if (paths[i] == encodedLocale) {
118 return i;
119 }
120 }
121 return static_cast<int8_t>(i);
122 }
123
124 /**
125 * @brief find locale is in typical locale.
126 *
127 * @param language
128 * @param script
129 * @param region
130 * @return true
131 * @return false
132 */
IsDefaultLocale(const char * language,const char * script,const char * region)133 bool IsDefaultLocale(const char *language, const char *script, const char *region)
134 {
135 uint64_t encodedLocale = Utils::EncodeLocale(language, script, region);
136 if (ClearRegion(encodedLocale) == LocaleMatcher::EN_QAAG_ENCODE) {
137 encodedLocale = Utils::EncodeLocale("en", "Latn", region);
138 }
139 size_t len = sizeof(TYPICAL_CODES_VALUE) / sizeof(TYPICAL_CODES_VALUE[0]);
140 for (size_t i = 0; i < len; i++) {
141 if (TYPICAL_CODES_VALUE[i] == encodedLocale) {
142 return true;
143 }
144 }
145 return false;
146 }
147
148 /**
149 * @brief find the default script of language and region.
150 * first search language and region corresponding script.
151 * if not found,search language corresponding script.
152 *
153 * @param language
154 * @param region
155 * @return uint32_t
156 */
FindDefaultScriptEncode(const char * language,const char * region)157 uint32_t FindDefaultScriptEncode(const char *language, const char *region)
158 {
159 uint64_t encodedLocale = Utils::EncodeLocale(language, nullptr, region);
160 size_t len = sizeof(LIKELY_TAGS_CODES_KEY) / sizeof(LIKELY_TAGS_CODES_KEY[0]);
161 for (size_t i = 0; i < len; i++) {
162 if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
163 return static_cast<uint32_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >>
164 BitOperatorNum::BIT_SIXTEEN);
165 }
166 }
167 if (region != nullptr) {
168 encodedLocale = Utils::EncodeLocale(language, nullptr, nullptr);
169 for (size_t i = 0; i < len; i++) {
170 if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
171 return static_cast<uint32_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >>
172 BitOperatorNum::BIT_SIXTEEN);
173 }
174 }
175 }
176 return LocaleMatcher::NULL_SCRIPT;
177 }
178
179 /**
180 * @brief find the default region of language and script.
181 * first search language and script corresponding region.
182 * if not found,search language corresponding region.
183 *
184 * @param language
185 * @param script
186 * @return uint16_t
187 */
FindDefaultRegionEncode(const char * language,const char * script)188 uint16_t FindDefaultRegionEncode(const char *language, const char *script)
189 {
190 /* first try language and script */
191 uint64_t encodedLocale = Utils::EncodeLocale(language, script, nullptr);
192 if (encodedLocale == Utils::EncodeLocale("en", "Qaag", nullptr)) {
193 encodedLocale = Utils::EncodeLocale("en", "Latn", nullptr);
194 }
195 size_t len = sizeof(LIKELY_TAGS_CODES_KEY) / sizeof(LIKELY_TAGS_CODES_KEY[0]);
196 for (size_t i = 0; i < len; i++) {
197 if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
198 return static_cast<uint16_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
199 }
200 }
201 /* if not found and script is not null,try language */
202 if (script != nullptr) {
203 encodedLocale = Utils::EncodeLocale(language, nullptr, nullptr);
204 for (size_t i = 0; i < len; i++) {
205 if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
206 return static_cast<uint16_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
207 }
208 }
209 }
210 return LocaleMatcher::NULL_REGION;
211 };
212
213 /**
214 * @brief find the first locale which in target path,is also in request path.
215 * return sum of the locale pos in target path and request path.
216 *
217 * @param requestPaths
218 * @param targetPaths
219 * @param len
220 * @return size_t
221 */
ComputeTrackPathDistance(const uint64_t * requestPaths,const uint64_t * targetPaths,size_t len)222 size_t ComputeTrackPathDistance(const uint64_t *requestPaths,
223 const uint64_t *targetPaths, size_t len)
224 {
225 size_t i = 0;
226 size_t j = 0;
227 for (i = 0; i < len; ++i) {
228 if (targetPaths[i] == LocaleMatcher::ROOT_LOCALE) {
229 // targetpath not in request path,so distance is 2*len
230 return len * 2;
231 }
232 for (j = 0; j < len; ++j) {
233 if (requestPaths[j] == targetPaths[i]) {
234 return i + j;
235 }
236 }
237 }
238 return len * 2; // targetpath not in request path,so distance is 2*len
239 }
240
CompareRegionWhenQaag(const ResLocale * current,const ResLocale * other,const ResLocale * request)241 int8_t CompareRegionWhenQaag(const ResLocale *current,
242 const ResLocale *other,
243 const ResLocale *request)
244 {
245 if ((request != nullptr) && (Utils::EncodeLocale(request->GetLanguage(), request->GetScript(),
246 nullptr) == LocaleMatcher::EN_QAAG_ENCODE)) {
247 if ((current != nullptr) && (Utils::EncodeLocale(current->GetLanguage(), nullptr,
248 current->GetRegion()) == LocaleMatcher::EN_GB_ENCODE)) {
249 return 1;
250 }
251 if ((other != nullptr) && (Utils::EncodeLocale(other->GetLanguage(), nullptr, other->GetRegion()) ==
252 LocaleMatcher::EN_GB_ENCODE)) {
253 return -1;
254 }
255 }
256 return 0;
257 }
258
259 /**
260 * @brief compare language,support new/old language code
261 * NEW_LANGUAGES_CODES is new language code,
262 * OLD_LANGUAGES_CODES is old language code.
263 * support iw/he,tl/fil,ji/yi,jw/jv,in/id.
264 * @param current
265 * @param other
266 * @return true
267 * @return false
268 */
CompareLanguage(const ResLocale * current,const ResLocale * other)269 bool CompareLanguage(const ResLocale *current, const ResLocale *other)
270 {
271 uint16_t currentEncodedLanguage =
272 Utils::EncodeLanguageByResLocale(current);
273 uint16_t otherEncodedLanguage = Utils::EncodeLanguageByResLocale(
274 other);
275 return ((currentEncodedLanguage == otherEncodedLanguage) ||
276 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[0]) &&
277 (otherEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
278 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[0]) &&
279 (currentEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
280 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[1]) &&
281 (otherEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
282 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[1]) &&
283 (currentEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
284 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[2]) &&
285 (otherEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
286 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[2]) &&
287 (currentEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
288 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[3]) &&
289 (otherEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
290 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[3]) &&
291 (currentEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
292 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[4]) &&
293 (otherEncodedLanguage == OLD_LANGUAGES_CODES[4])) ||
294 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[4]) &&
295 (currentEncodedLanguage == OLD_LANGUAGES_CODES[4])));
296 }
297
CompareScript(const ResLocale * current,const ResLocale * other)298 bool CompareScript(const ResLocale *current, const ResLocale *other)
299 {
300 uint32_t currentEncodedScript = 0;
301 uint32_t otherEncodedScript = 0;
302 if ((current != nullptr) && (current->GetScript() == nullptr)) {
303 currentEncodedScript = FindDefaultScriptEncode(current->GetLanguage(),
304 current->GetRegion());
305 } else {
306 currentEncodedScript = Utils::EncodeScriptByResLocale(current);
307 }
308 if ((other != nullptr) && (other->GetScript() == nullptr)) {
309 otherEncodedScript = FindDefaultScriptEncode(other->GetLanguage(),
310 other->GetRegion());
311 } else {
312 otherEncodedScript = Utils::EncodeScriptByResLocale(other);
313 }
314 if (current != nullptr && other != nullptr) {
315 // when current locale is en-Qaag is equal en-Latn
316 if (Utils::EncodeLocale(current->GetLanguage(), current->GetScript(), nullptr) ==
317 Utils::EncodeLocale("en", "Qaag", nullptr)) {
318 if (Utils::EncodeLocale(other->GetLanguage(), other->GetScript(), nullptr) ==
319 Utils::EncodeLocale("en", "Latn", nullptr)) {
320 return true;
321 }
322 }
323 }
324 bool compareRegion = false;
325 if ((currentEncodedScript == LocaleMatcher::NULL_SCRIPT) || (otherEncodedScript == LocaleMatcher::NULL_SCRIPT)) {
326 // if request script is null, region must be same
327 compareRegion = true;
328 }
329 if (compareRegion) {
330 uint16_t currentRegionEncode = Utils::EncodeRegionByResLocale(current);
331 uint16_t otherRegionEncode = Utils::EncodeRegionByResLocale(other);
332 return (otherRegionEncode == LocaleMatcher::NULL_REGION) || (currentRegionEncode == otherRegionEncode);
333 }
334 return currentEncodedScript == otherEncodedScript;
335 }
336
AlphabeticallyCompare(const ResLocale * current,uint64_t currentEncodedLocale,const ResLocale * other,uint64_t otherEncodedLocale)337 int8_t AlphabeticallyCompare(const ResLocale *current,
338 uint64_t currentEncodedLocale,
339 const ResLocale *other,
340 uint64_t otherEncodedLocale)
341 {
342 if (currentEncodedLocale == otherEncodedLocale) {
343 return 0;
344 }
345 if (current == nullptr || current->GetRegion() == nullptr) {
346 return -1;
347 }
348 if (other == nullptr || other->GetRegion() == nullptr) {
349 return 1;
350 }
351 // be here region is not null
352 char currentFirstChar = (current->GetRegion())[0];
353 char otherFirstChar = (other->GetRegion())[0];
354 if (currentFirstChar >= '0' && currentFirstChar <= '9') {
355 if (otherFirstChar < '0' || otherFirstChar > '9') {
356 return -1;
357 }
358 } else {
359 if (otherFirstChar >= '0' && otherFirstChar <= '9') {
360 return 1;
361 }
362 }
363 if (currentEncodedLocale > otherEncodedLocale) {
364 return -1;
365 }
366 if (otherEncodedLocale > currentEncodedLocale) {
367 return 1;
368 }
369 return 0;
370 }
371
CompareWhenRegionIsNull(uint16_t currentEncodedRegion,uint16_t otherEncodedRegion,const ResLocale * current,const ResLocale * other,const ResLocale * request)372 int8_t CompareWhenRegionIsNull(uint16_t currentEncodedRegion, uint16_t otherEncodedRegion,
373 const ResLocale *current,
374 const ResLocale *other,
375 const ResLocale *request)
376 {
377 if (current == nullptr || current->GetRegion() == nullptr) {
378 return 1;
379 }
380 if (other == nullptr || other->GetRegion() == nullptr) {
381 return -1;
382 }
383 int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
384 if (qaagResult != 0) {
385 return qaagResult;
386 }
387 // get request default region
388 uint16_t requestDefaultRegion =
389 FindDefaultRegionEncode((request == nullptr) ? nullptr : request->GetLanguage(),
390 (request == nullptr) ? nullptr : request->GetScript());
391 if (requestDefaultRegion == currentEncodedRegion) {
392 return 1;
393 }
394 if (requestDefaultRegion == otherEncodedRegion) {
395 return -1;
396 }
397 // current and other region is not null.alphabetically
398 uint64_t currentEncodedLocale = Utils::EncodeLocale((request == nullptr) ? nullptr : request->GetLanguage(),
399 nullptr, (current == nullptr) ? nullptr : current->GetRegion());
400 uint64_t otherEncodedLocale = Utils::EncodeLocale(
401 (request == nullptr) ? nullptr : request->GetLanguage(), nullptr, other->GetRegion());
402 return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
403 }
404
CompareDistance(uint64_t currentEncodedLocale,uint64_t otherEncodedLocale,const uint64_t * requestEncodedTrackPath,const ResLocale * request)405 int8_t CompareDistance(uint64_t currentEncodedLocale, uint64_t otherEncodedLocale,
406 const uint64_t *requestEncodedTrackPath, const ResLocale *request)
407 {
408 uint64_t currentEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
409 FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, currentEncodedLocale, currentEncodedTrackPath);
410 uint64_t otherEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
411 FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, otherEncodedLocale, otherEncodedTrackPath);
412 const size_t currentDistance = ComputeTrackPathDistance(
413 requestEncodedTrackPath, currentEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
414 const size_t targetDistance = ComputeTrackPathDistance(
415 requestEncodedTrackPath, otherEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
416 if (currentDistance < targetDistance) {
417 return 1;
418 }
419 if (currentDistance > targetDistance) {
420 return -1;
421 }
422 return 0;
423 }
424
CompareDefaultRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)425 int8_t CompareDefaultRegion(const ResLocale *current,
426 const ResLocale *other,
427 const ResLocale *request)
428 {
429 int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
430 if (qaagResult != 0) {
431 return qaagResult;
432 } else {
433 bool isCurrentDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
434 (request == nullptr) ? nullptr : request->GetScript(),
435 (current == nullptr) ? nullptr : current->GetRegion());
436 bool isOtherDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
437 (request == nullptr) ? nullptr : request->GetScript(), (other == nullptr) ? nullptr : other->GetRegion());
438 if (isCurrentDefaultRegion != isOtherDefaultRegion) {
439 if (isCurrentDefaultRegion) {
440 return 1;
441 } else {
442 return -1;
443 }
444 }
445 }
446 return 0;
447 }
448
449 /**
450 * @brief compare current and target region, which is better for request.
451 * @param current current locale
452 * @param target target locale
453 * @param request request locale
454 * @return int8_t if current region is better than target region,return 1. if current region is equal target region,
455 * return 0. If target region is better than current region, return -1.
456 */
CompareRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)457 int8_t CompareRegion(const ResLocale *current,
458 const ResLocale *other,
459 const ResLocale *request)
460 {
461 uint16_t currentEncodedRegion = Utils::EncodeRegionByResLocale(current);
462 uint16_t otherEncodedRegion = Utils::EncodeRegionByResLocale(other);
463 if (request == nullptr || request->GetRegion() == nullptr) {
464 return CompareWhenRegionIsNull(currentEncodedRegion, otherEncodedRegion, current, other, request);
465 }
466 uint64_t requestEncodedLocale = Utils::EncodeLocale(
467 request->GetLanguage(), nullptr, request->GetRegion());
468 uint64_t requestEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
469 FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, requestEncodedLocale, requestEncodedTrackPath);
470 uint64_t currentEncodedLocale = Utils::EncodeLocale(
471 request->GetLanguage(), nullptr, (current == nullptr) ? nullptr : current->GetRegion());
472 uint64_t otherEncodedLocale = Utils::EncodeLocale(
473 request->GetLanguage(), nullptr, (other == nullptr) ? nullptr : other->GetRegion());
474 int8_t currentMatchDistance = SearchTrackPathDistance(
475 requestEncodedTrackPath,
476 LocaleMatcher::TRACKPATH_ARRAY_SIZE,
477 currentEncodedLocale);
478 int8_t otherMatchDistance = SearchTrackPathDistance(
479 requestEncodedTrackPath,
480 LocaleMatcher::TRACKPATH_ARRAY_SIZE,
481 otherEncodedLocale);
482 if (currentMatchDistance < otherMatchDistance) {
483 return 1;
484 }
485 if (currentMatchDistance > otherMatchDistance) {
486 return -1;
487 }
488 int8_t result = CompareDistance(currentEncodedLocale, otherEncodedLocale, requestEncodedTrackPath, request);
489 if (result != 0) {
490 return result;
491 }
492 result = CompareDefaultRegion(current, other, request);
493 if (result != 0) {
494 return result;
495 }
496 uint16_t requestDefaultRegion =
497 FindDefaultRegionEncode(request->GetLanguage(), request->GetScript());
498 if (requestDefaultRegion == currentEncodedRegion) {
499 return 1;
500 }
501 if (requestDefaultRegion == otherEncodedRegion) {
502 return -1;
503 }
504 return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
505 }
506
Match(const ResLocale * current,const ResLocale * other)507 bool LocaleMatcher::Match(const ResLocale *current, const ResLocale *other)
508 {
509 if (current == nullptr || other == nullptr) {
510 return true;
511 }
512 // language is not null.
513 bool isLanguageEqual = CompareLanguage(current, other);
514 if (!isLanguageEqual) {
515 return false;
516 }
517 return CompareScript(current, other);
518 };
519
Normalize(ResLocale * localeInfo)520 bool LocaleMatcher::Normalize(ResLocale *localeInfo)
521 {
522 if (localeInfo == nullptr) {
523 return true;
524 }
525 if (!Utils::IsStrEmpty(localeInfo->GetScript())) {
526 return true;
527 }
528 uint32_t encodedScript = FindDefaultScriptEncode(localeInfo->GetLanguage(),
529 localeInfo->GetRegion());
530 if (encodedScript == LocaleMatcher::NULL_SCRIPT) {
531 return true;
532 }
533 char *tempScript = new(std::nothrow) char[SCRIPT_ARRAY_LEN];
534 if (tempScript == nullptr) {
535 return false;
536 }
537 tempScript[SCRIPT_LEN] = '\0';
538 Utils::DecodeScript(encodedScript, tempScript);
539 localeInfo->script_ = tempScript;
540 return true;
541 }
542
CompareLanguageIgnoreOldNewCode(const ResLocale * current,const ResLocale * other,const ResLocale * request)543 int8_t CompareLanguageIgnoreOldNewCode(const ResLocale *current, const ResLocale *other, const ResLocale *request)
544 {
545 uint16_t currentLanguageEncode = Utils::EncodeLanguageByResLocale(current);
546 uint16_t otherLanguageEncode = Utils::EncodeLanguageByResLocale(other);
547 uint16_t requestLanguageEncode = Utils::EncodeLanguageByResLocale(request);
548 if ((currentLanguageEncode == requestLanguageEncode) && (otherLanguageEncode != requestLanguageEncode)) {
549 return 1;
550 }
551 if ((otherLanguageEncode == requestLanguageEncode) && (currentLanguageEncode != requestLanguageEncode)) {
552 return -1;
553 }
554 return 0;
555 }
556
IsSimilarToUsEnglish(const ResLocale * localeInfo)557 bool IsSimilarToUsEnglish(const ResLocale *localeInfo)
558 {
559 uint64_t localeEncode = Utils::EncodeLocale("en", nullptr,
560 (localeInfo == nullptr) ? nullptr : localeInfo->GetRegion());
561 uint64_t loclaeEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
562 FindTrackPath(nullptr, LocaleMatcher::TRACKPATH_ARRAY_SIZE, localeEncode, loclaeEncodedTrackPath);
563 uint8_t len = LocaleMatcher::TRACKPATH_ARRAY_SIZE;
564 for (uint8_t i = 0; i < len; ++i) {
565 if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, nullptr)) {
566 return true;
567 }
568 if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, "001")) {
569 return false;
570 }
571 }
572 return false;
573 }
CompareRegionWhenLangIsNotEqual(const ResLocale * current,const ResLocale * other,const ResLocale * request)574 bool CompareRegionWhenLangIsNotEqual(const ResLocale *current,
575 const ResLocale *other,
576 const ResLocale *request)
577 {
578 int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
579 if (qaagResult != 0) {
580 return qaagResult;
581 }
582 if (request != nullptr && (Utils::EncodeLanguage(request->GetLanguage())) == Utils::EncodeLanguage("en")) {
583 // when request is en-us,empty region is better
584 if ((Utils::EncodeRegion(request->GetRegion())) == Utils::EncodeRegion("US")) {
585 if (current != nullptr) {
586 return (current->GetRegion() == nullptr) ||
587 ((Utils::EncodeRegion(current->GetRegion())) == Utils::EncodeRegion("US"));
588 } else {
589 return !(other->GetRegion() == nullptr ||
590 ((Utils::EncodeRegion(other->GetRegion())) == Utils::EncodeRegion("US")));
591 }
592 } else if (IsSimilarToUsEnglish(request)) {
593 if (current != nullptr) {
594 return IsSimilarToUsEnglish(current);
595 } else {
596 return !IsSimilarToUsEnglish(other);
597 }
598 }
599 }
600 return current != nullptr;
601 }
602
IsMoreSuitable(const ResLocale * current,const ResLocale * other,const ResLocale * request)603 int8_t LocaleMatcher::IsMoreSuitable(const ResLocale *current,
604 const ResLocale *other,
605 const ResLocale *request)
606 {
607 if (request == nullptr) {
608 // if request ResLocale is nullptr, the candidate is also nullptr will be more suitable
609 if (current != nullptr && other == nullptr) {
610 // -1 means other is more suitable
611 return -1;
612 }
613 if (current == nullptr && other != nullptr) {
614 // 1 means current is more suitable
615 return 1;
616 }
617 return 0;
618 }
619 if (current == nullptr && other == nullptr) {
620 return 0;
621 }
622 bool isLangEqual = CompareLanguage(current, other);
623 if (!isLangEqual) {
624 // current or other language is null, not null language is better
625 bool result = CompareRegionWhenLangIsNotEqual(current, other, request);
626 return result ? 1 : -1;
627 }
628 uint16_t currentEncodedRegion =
629 Utils::EncodeRegionByResLocale(current);
630 uint16_t otherEncodedRegion =
631 Utils::EncodeRegionByResLocale(other);
632 if (currentEncodedRegion == otherEncodedRegion) {
633 // same language,same script,same region
634 return CompareLanguageIgnoreOldNewCode(current, other, request);
635 }
636 // equal request region is better
637 uint16_t requestEncodedRegion = Utils::EncodeRegionByResLocale(request);
638 if (currentEncodedRegion == requestEncodedRegion) {
639 return 1;
640 }
641 if (otherEncodedRegion == requestEncodedRegion) {
642 return -1;
643 }
644 int8_t isRegionEqual = CompareRegion(current, other, request);
645 if (isRegionEqual == 0) {
646 return CompareLanguageIgnoreOldNewCode(current, other, request);
647 }
648 return isRegionEqual;
649 };
650
651 /**
652 * @brief language tag is 2 or 3 letters
653 *
654 * @param str
655 * @param len
656 * @return true
657 * @return false
658 */
IsLanguageTag(const char * str,int32_t len)659 bool LocaleMatcher::IsLanguageTag(const char *str, int32_t len)
660 {
661 if (len < 0) {
662 len = strlen(str);
663 }
664 if (len >= 2 && len <= 3 && Utils::IsAlphaString(str, len)) { // 2 or 3 means Language legal length
665 return true;
666 }
667 return false;
668 }
669
670 /**
671 * @brief script is 4 letters
672 *
673 * @param str
674 * @param len
675 * @return true
676 * @return false
677 */
IsScriptTag(const char * str,int32_t len)678 bool LocaleMatcher::IsScriptTag(const char *str, int32_t len)
679 {
680 if (len < 0) {
681 len = strlen(str);
682 }
683 if (len == 4 && Utils::IsAlphaString(str, len)) { // 4 means script legal length
684 return true;
685 }
686 return false;
687 }
688
689 /**
690 * @brief region is 2 letters or 3 digits.
691 *
692 * @param str
693 * @param len
694 * @return true
695 * @return false
696 */
IsRegionTag(const char * str,int32_t len)697 bool LocaleMatcher::IsRegionTag(const char *str, int32_t len)
698 {
699 if (len < 0) {
700 len = strlen(str);
701 }
702 if (len == 2 && Utils::IsAlphaString(str, len)) { // 2 means region legal length
703 return true;
704 }
705 if (len == 3 && Utils::IsNumericString(str, len)) { // 3 menas region legal length
706 return true;
707 }
708 return false;
709 }
710
IsMoreSpecificThan(const ResLocale * current,const ResLocale * other)711 int8_t LocaleMatcher::IsMoreSpecificThan(const ResLocale *current, const ResLocale *other)
712 {
713 // compare language
714 if (current == nullptr && other == nullptr) {
715 return 0;
716 }
717 if (current != nullptr && other == nullptr) {
718 return 1;
719 }
720 if (current == nullptr && other != nullptr) {
721 return -1;
722 }
723 // here language is equal,compare region
724 if (current->GetRegion() == other->GetRegion()) {
725 return 0;
726 }
727 if (current->GetRegion() == nullptr) {
728 return -1;
729 }
730 if (other->GetRegion() == nullptr) {
731 return 1;
732 }
733 return 0;
734 }
735 } // namespace Resource
736 } // namespace Global
737 } // namespace OHOS