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