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 script 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 * support 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 return ((currentEncodedLanguage == otherEncodedLanguage) ||
273 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[0])
274 && (otherEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
275 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[0])
276 && (currentEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
277 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[1])
278 && (otherEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
279 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[1])
280 && (currentEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
281 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[2])
282 && (otherEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
283 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[2])
284 && (currentEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
285 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[3])
286 && (otherEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
287 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[3])
288 && (currentEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
289 ((currentEncodedLanguage == NEW_LANGUAGES_CODES[4])
290 && (otherEncodedLanguage == OLD_LANGUAGES_CODES[4])) ||
291 ((otherEncodedLanguage == NEW_LANGUAGES_CODES[4])
292 && (currentEncodedLanguage == OLD_LANGUAGES_CODES[4])));
293 }
294
CompareScript(const ResLocale * current,const ResLocale * other)295 bool CompareScript(const ResLocale *current, const ResLocale *other)
296 {
297 uint32_t currentEncodedScript = 0;
298 uint32_t otherEncodedScript = 0;
299 if ((current != nullptr) && (current->GetScript() == nullptr)) {
300 currentEncodedScript = FindDefaultScriptEncode(current->GetLanguage(),
301 current->GetRegion());
302 } else {
303 currentEncodedScript = Utils::EncodeScriptByResLocale(current);
304 }
305 if ((other != nullptr) && (other->GetScript() == nullptr)) {
306 otherEncodedScript = FindDefaultScriptEncode(other->GetLanguage(),
307 other->GetRegion());
308 } else {
309 otherEncodedScript = Utils::EncodeScriptByResLocale(other);
310 }
311 if (current != nullptr && other != nullptr) {
312 // when current locale is en-Qaag is equal en-Latn
313 if (Utils::EncodeLocale(current->GetLanguage(), current->GetScript(), nullptr) ==
314 Utils::EncodeLocale("en", "Qaag", nullptr)) {
315 if (Utils::EncodeLocale(other->GetLanguage(), other->GetScript(), nullptr) ==
316 Utils::EncodeLocale("en", "Latn", nullptr)) {
317 return true;
318 }
319 }
320 }
321 bool compareRegion = false;
322 if ((currentEncodedScript == LocaleMatcher::NULL_SCRIPT) || (otherEncodedScript == LocaleMatcher::NULL_SCRIPT)) {
323 // if request script is null, region must be same
324 compareRegion = true;
325 }
326 if (compareRegion) {
327 uint16_t currentRegionEncode = Utils::EncodeRegionByResLocale(current);
328 uint16_t otherRegionEncode = Utils::EncodeRegionByResLocale(other);
329 return (otherRegionEncode == LocaleMatcher::NULL_REGION) || (currentRegionEncode == otherRegionEncode);
330 }
331 return currentEncodedScript == otherEncodedScript;
332 }
333
AlphabeticallyCompare(const ResLocale * current,uint64_t currentEncodedLocale,const ResLocale * other,uint64_t otherEncodedLocale)334 int8_t AlphabeticallyCompare(const ResLocale *current,
335 uint64_t currentEncodedLocale,
336 const ResLocale *other,
337 uint64_t otherEncodedLocale)
338 {
339 if (currentEncodedLocale == otherEncodedLocale) {
340 return 0;
341 }
342 if (current == nullptr || current->GetRegion() == nullptr) {
343 return -1;
344 }
345 if (other == nullptr || other->GetRegion() == nullptr) {
346 return 1;
347 }
348 // be here region is not null
349 char currentFirstChar = (current->GetRegion())[0];
350 char otherFirstChar = (other->GetRegion())[0];
351 if (currentFirstChar >= '0' && currentFirstChar <= '9') {
352 if (otherFirstChar < '0' || otherFirstChar > '9') {
353 return -1;
354 }
355 } else {
356 if (otherFirstChar >= '0' && otherFirstChar <= '9') {
357 return 1;
358 }
359 }
360 if (currentEncodedLocale > otherEncodedLocale) {
361 return -1;
362 }
363 if (otherEncodedLocale > currentEncodedLocale) {
364 return 1;
365 }
366 return 0;
367 }
368
CompareWhenRegionIsNull(uint16_t currentEncodedRegion,uint16_t otherEncodedRegion,const ResLocale * current,const ResLocale * other,const ResLocale * request)369 int8_t CompareWhenRegionIsNull(uint16_t currentEncodedRegion, uint16_t otherEncodedRegion,
370 const ResLocale *current,
371 const ResLocale *other,
372 const ResLocale *request)
373 {
374 if (current == nullptr || current->GetRegion() == nullptr) {
375 return 1;
376 }
377 if (other == nullptr || other->GetRegion() == nullptr) {
378 return -1;
379 }
380 int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
381 if (qaagResult != 0) {
382 return qaagResult;
383 }
384 // get request default region
385 uint16_t requestDefaultRegion =
386 FindDefaultRegionEncode((request == nullptr) ? nullptr : request->GetLanguage(),
387 (request == nullptr) ? nullptr : request->GetScript());
388 if (requestDefaultRegion == currentEncodedRegion) {
389 return 1;
390 }
391 if (requestDefaultRegion == otherEncodedRegion) {
392 return -1;
393 }
394 // current and other region is not null.alphabetically
395 uint64_t currentEncodedLocale = Utils::EncodeLocale((request == nullptr) ? nullptr : request->GetLanguage(),
396 nullptr, (current == nullptr) ? nullptr : current->GetRegion());
397 uint64_t otherEncodedLocale = Utils::EncodeLocale(
398 (request == nullptr) ? nullptr : request->GetLanguage(), nullptr, other->GetRegion());
399 return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
400 }
401
CompareDistance(uint64_t currentEncodedLocale,uint64_t otherEncodedLocale,const uint64_t * requestEncodedTrackPath,const ResLocale * request)402 int8_t CompareDistance(uint64_t currentEncodedLocale, uint64_t otherEncodedLocale,
403 const uint64_t *requestEncodedTrackPath, const ResLocale *request)
404 {
405 uint64_t currentEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
406 FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, currentEncodedLocale, currentEncodedTrackPath);
407 uint64_t otherEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
408 FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, otherEncodedLocale, otherEncodedTrackPath);
409 const size_t currentDistance = ComputeTrackPathDistance(
410 requestEncodedTrackPath, currentEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
411 const size_t targetDistance = ComputeTrackPathDistance(
412 requestEncodedTrackPath, otherEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
413 if (currentDistance < targetDistance) {
414 return 1;
415 }
416 if (currentDistance > targetDistance) {
417 return -1;
418 }
419 return 0;
420 }
421
CompareDefaultRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)422 int8_t CompareDefaultRegion(const ResLocale *current,
423 const ResLocale *other,
424 const ResLocale *request)
425 {
426 int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
427 if (qaagResult != 0) {
428 return qaagResult;
429 } else {
430 bool isCurrentDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
431 (request == nullptr) ? nullptr : request->GetScript(),
432 (current == nullptr) ? nullptr : current->GetRegion());
433 bool isOtherDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
434 (request == nullptr) ? nullptr : request->GetScript(), (other == nullptr) ? nullptr : other->GetRegion());
435 if (isCurrentDefaultRegion != isOtherDefaultRegion) {
436 if (isCurrentDefaultRegion) {
437 return 1;
438 } else {
439 return -1;
440 }
441 }
442 }
443 return 0;
444 }
445
446 /**
447 * @brief compare current and target region, which is better for request.
448 * @param current current locale
449 * @param target target locale
450 * @param request request locale
451 * @return int8_t if current region is better than target region,return 1. if current region is equal target region,
452 * return 0. If target region is better than current region, return -1.
453 */
CompareRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)454 int8_t CompareRegion(const ResLocale *current,
455 const ResLocale *other,
456 const ResLocale *request)
457 {
458 uint16_t currentEncodedRegion = Utils::EncodeRegionByResLocale(current);
459 uint16_t otherEncodedRegion = Utils::EncodeRegionByResLocale(other);
460 if (request == nullptr || request->GetRegion() == nullptr) {
461 return CompareWhenRegionIsNull(currentEncodedRegion, otherEncodedRegion, current, other, request);
462 }
463 uint64_t requestEncodedLocale = Utils::EncodeLocale(
464 request->GetLanguage(), nullptr, request->GetRegion());
465 uint64_t requestEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
466 FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, requestEncodedLocale, requestEncodedTrackPath);
467 uint64_t currentEncodedLocale = Utils::EncodeLocale(
468 request->GetLanguage(), nullptr, (current == nullptr) ? nullptr : current->GetRegion());
469 uint64_t otherEncodedLocale = Utils::EncodeLocale(
470 request->GetLanguage(), nullptr, (other == nullptr) ? nullptr : other->GetRegion());
471 int8_t currentMatchDistance = SearchTrackPathDistance(
472 requestEncodedTrackPath,
473 LocaleMatcher::TRACKPATH_ARRAY_SIZE,
474 currentEncodedLocale);
475 int8_t otherMatchDistance = SearchTrackPathDistance(
476 requestEncodedTrackPath,
477 LocaleMatcher::TRACKPATH_ARRAY_SIZE,
478 otherEncodedLocale);
479 if (currentMatchDistance < otherMatchDistance) {
480 return 1;
481 }
482 if (currentMatchDistance > otherMatchDistance) {
483 return -1;
484 }
485 int8_t result = CompareDistance(currentEncodedLocale, otherEncodedLocale, requestEncodedTrackPath, request);
486 if (result != 0) {
487 return result;
488 }
489 result = CompareDefaultRegion(current, other, request);
490 if (result != 0) {
491 return result;
492 }
493 uint16_t requestDefaultRegion =
494 FindDefaultRegionEncode(request->GetLanguage(), request->GetScript());
495 if (requestDefaultRegion == currentEncodedRegion) {
496 return 1;
497 }
498 if (requestDefaultRegion == otherEncodedRegion) {
499 return -1;
500 }
501 return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
502 }
503
Match(const ResLocale * current,const ResLocale * other)504 bool LocaleMatcher::Match(const ResLocale *current, const ResLocale *other)
505 {
506 if (current == nullptr || other == nullptr) {
507 return true;
508 }
509 // language is not null.
510 bool isLanguageEqual = CompareLanguage(current, other);
511 if (!isLanguageEqual) {
512 return false;
513 }
514 return CompareScript(current, other);
515 };
516
Normalize(ResLocale * localeInfo)517 bool LocaleMatcher::Normalize(ResLocale *localeInfo)
518 {
519 if (localeInfo == nullptr) {
520 return true;
521 }
522 if (!Utils::IsStrEmpty(localeInfo->GetScript())) {
523 return true;
524 }
525 uint32_t encodedScript = FindDefaultScriptEncode(localeInfo->GetLanguage(),
526 localeInfo->GetRegion());
527 if (encodedScript == LocaleMatcher::NULL_SCRIPT) {
528 return true;
529 }
530 char *tempScript = new(std::nothrow) char[SCRIPT_ARRAY_LEN];
531 if (tempScript == nullptr) {
532 return false;
533 }
534 tempScript[SCRIPT_LEN] = '\0';
535 Utils::DecodeScript(encodedScript, tempScript);
536 localeInfo->script_ = tempScript;
537 return true;
538 }
539
CompareLanguageIgnoreOldNewCode(const ResLocale * current,const ResLocale * other,const ResLocale * request)540 int8_t CompareLanguageIgnoreOldNewCode(const ResLocale *current, const ResLocale *other, const ResLocale *request)
541 {
542 uint16_t currentLanguageEncode = Utils::EncodeLanguageByResLocale(current);
543 uint16_t otherLanguageEncode = Utils::EncodeLanguageByResLocale(other);
544 uint16_t requestLanguageEncode = Utils::EncodeLanguageByResLocale(request);
545 if ((currentLanguageEncode == requestLanguageEncode) && (otherLanguageEncode != requestLanguageEncode)) {
546 return 1;
547 }
548 if ((otherLanguageEncode == requestLanguageEncode) && (currentLanguageEncode != requestLanguageEncode)) {
549 return -1;
550 }
551 return 0;
552 }
553
IsSimilarToUsEnglish(const ResLocale * localeInfo)554 bool IsSimilarToUsEnglish(const ResLocale *localeInfo)
555 {
556 uint64_t localeEncode = Utils::EncodeLocale("en", nullptr,
557 (localeInfo == nullptr) ? nullptr : localeInfo->GetRegion());
558 uint64_t loclaeEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
559 FindTrackPath(nullptr, LocaleMatcher::TRACKPATH_ARRAY_SIZE, localeEncode, loclaeEncodedTrackPath);
560 uint8_t len = LocaleMatcher::TRACKPATH_ARRAY_SIZE;
561 for (uint8_t i = 0; i < len; ++i) {
562 if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, nullptr)) {
563 return true;
564 }
565 if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, "001")) {
566 return false;
567 }
568 }
569 return false;
570 }
CompareRegionWhenLangIsNotEqual(const ResLocale * current,const ResLocale * other,const ResLocale * request)571 bool CompareRegionWhenLangIsNotEqual(const ResLocale *current,
572 const ResLocale *other,
573 const ResLocale *request)
574 {
575 int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
576 if (qaagResult != 0) {
577 return qaagResult;
578 }
579 if (request != nullptr && (Utils::EncodeLanguage(request->GetLanguage())) == Utils::EncodeLanguage("en")) {
580 // when request is en-us,empty region is better
581 if ((Utils::EncodeRegion(request->GetRegion())) == Utils::EncodeRegion("US")) {
582 if (current != nullptr) {
583 return (current->GetRegion() == nullptr) ||
584 ((Utils::EncodeRegion(current->GetRegion())) == Utils::EncodeRegion("US"));
585 } else {
586 return !(other->GetRegion() == nullptr ||
587 ((Utils::EncodeRegion(other->GetRegion())) == Utils::EncodeRegion("US")));
588 }
589 } else if (IsSimilarToUsEnglish(request)) {
590 if (current != nullptr) {
591 return IsSimilarToUsEnglish(current);
592 } else {
593 return !IsSimilarToUsEnglish(other);
594 }
595 }
596 }
597 return current != nullptr;
598 }
599
IsMoreSuitable(const ResLocale * current,const ResLocale * other,const ResLocale * request)600 int8_t LocaleMatcher::IsMoreSuitable(const ResLocale *current,
601 const ResLocale *other,
602 const ResLocale *request)
603 {
604 if (request == nullptr) {
605 // if request ResLocale is nullptr, the candidate is also nullptr will be more suitable
606 if (current != nullptr && other == nullptr) {
607 // -1 means other is more suitable
608 return -1;
609 }
610 if (current == nullptr && other != nullptr) {
611 // 1 means current is more suitable
612 return 1;
613 }
614 return 0;
615 }
616 if (current == nullptr && other == nullptr) {
617 return 0;
618 }
619 bool isLangEqual = CompareLanguage(current, other);
620 if (!isLangEqual) {
621 // current or other language is null, not null language is better
622 bool result = CompareRegionWhenLangIsNotEqual(current, other, request);
623 return result ? 1 : -1;
624 }
625 uint16_t currentEncodedRegion =
626 Utils::EncodeRegionByResLocale(current);
627 uint16_t otherEncodedRegion =
628 Utils::EncodeRegionByResLocale(other);
629 if (currentEncodedRegion == otherEncodedRegion) {
630 // same language,same script,same region
631 return CompareLanguageIgnoreOldNewCode(current, other, request);
632 }
633 // equal request region is better
634 uint16_t requestEncodedRegion = Utils::EncodeRegionByResLocale(request);
635 if (currentEncodedRegion == requestEncodedRegion) {
636 return 1;
637 }
638 if (otherEncodedRegion == requestEncodedRegion) {
639 return -1;
640 }
641 int8_t isRegionEqual = CompareRegion(current, other, request);
642 if (isRegionEqual == 0) {
643 return CompareLanguageIgnoreOldNewCode(current, other, request);
644 }
645 return isRegionEqual;
646 };
647
648 /**
649 * @brief language tag is 2 or 3 letters
650 *
651 * @param str
652 * @param len
653 * @return true
654 * @return false
655 */
IsLanguageTag(const char * str,int32_t len)656 bool LocaleMatcher::IsLanguageTag(const char *str, int32_t len)
657 {
658 if (len < 0) {
659 len = strlen(str);
660 }
661 if (len >= 2 && len <= 3 && Utils::IsAlphaString(str, len)) {
662 return true;
663 }
664 return false;
665 }
666
667 /**
668 * @brief script is 4 letters
669 *
670 * @param str
671 * @param len
672 * @return true
673 * @return false
674 */
IsScriptTag(const char * str,int32_t len)675 bool LocaleMatcher::IsScriptTag(const char *str, int32_t len)
676 {
677 if (len < 0) {
678 len = strlen(str);
679 }
680 if (len == 4 && Utils::IsAlphaString(str, len)) {
681 return true;
682 }
683 return false;
684 }
685
686 /**
687 * @brief region is 2 letters or 3 digits.
688 *
689 * @param str
690 * @param len
691 * @return true
692 * @return false
693 */
IsRegionTag(const char * str,int32_t len)694 bool LocaleMatcher::IsRegionTag(const char *str, int32_t len)
695 {
696 if (len < 0) {
697 len = strlen(str);
698 }
699 if (len == 2 && Utils::IsAlphaString(str, len)) {
700 return true;
701 }
702 if (len == 3 && Utils::IsNumericString(str, len)) {
703 return true;
704 }
705 return false;
706 }
707
IsMoreSpecificThan(const ResLocale * current,const ResLocale * other)708 int8_t LocaleMatcher::IsMoreSpecificThan(const ResLocale *current, const ResLocale *other)
709 {
710 // compare language
711 if (current == nullptr && other == nullptr) {
712 return 0;
713 }
714 if (current != nullptr && other == nullptr) {
715 return 1;
716 }
717 if (current == nullptr && other != nullptr) {
718 return -1;
719 }
720 // here language is equal,compare region
721 if (current->GetRegion() == other->GetRegion()) {
722 return 0;
723 }
724 if (current->GetRegion() == nullptr) {
725 return -1;
726 }
727 if (other->GetRegion() == nullptr) {
728 return 1;
729 }
730 return 0;
731 }
732 } // namespace Resource
733 } // namespace Global
734 } // namespace OHOS