• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2025 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 "plugins/ets/stdlib/native/core/IntlLocale.h"
16 #include "plugins/ets/stdlib/native/core/IntlLanguageTag.h"
17 #include "plugins/ets/stdlib/native/core/IntlCommon.h"
18 #include "plugins/ets/stdlib/native/core/stdlib_ani_helpers.h"
19 #include <unicode/locid.h>
20 #include <unicode/numsys.h>
21 #include <unicode/localpointer.h>
22 #include "libpandabase/macros.h"
23 
24 #include <cassert>
25 #include <cstring>
26 #include <set>
27 #include <array>
28 #include <sstream>
29 #include <vector>
30 #include <algorithm>
31 
32 namespace ark::ets::stdlib::intl {
33 
34 enum LocaleInfo { LANG, SCRIPT, COUNTRY, U_CA, U_KF, U_CO, U_HC, U_NU, U_KN, COUNT };
35 
36 struct LocaleCheckInfo {
37     std::set<std::string> langs;
38     std::set<std::string> regions;
39     std::set<std::string> scripts;
40 };
41 
GetLocaleCheckInfo()42 static LocaleCheckInfo &GetLocaleCheckInfo()
43 {
44     static LocaleCheckInfo gCheckInfo;
45     return gCheckInfo;
46 }
47 
GetLocaleInfo()48 static std::vector<std::string> &GetLocaleInfo()
49 {
50     static std::vector<std::string> gLocaleInfo(LocaleInfo::COUNT, "");
51     std::fill(gLocaleInfo.begin(), gLocaleInfo.end(), "");
52     return gLocaleInfo;
53 }
54 
HandleLocale(const std::string & localeString)55 ParsedLocale HandleLocale(const std::string &localeString)
56 {
57     size_t len = localeString.size();
58     ParsedLocale parsedResult;
59 
60     // a. The single-character subtag ’x’ as the primary subtag indicates
61     //    that the language tag consists solely of subtags whose meaning is
62     //    defined by private agreement.
63     // b. Extensions cannot be used in tags that are entirely private use.
64     if (intl::IsPrivateSubTag(localeString, len)) {
65         parsedResult.base = localeString;
66         return parsedResult;
67     }
68     // If cannot find "-u-", return the whole string as base.
69     size_t foundExtension = localeString.find("-u-");
70     if (foundExtension == std::string::npos) {
71         parsedResult.base = localeString;
72         return parsedResult;
73     }
74     // Let privateIndex be Call(%StringProto_indexOf%, foundLocale, « "-x-" »).
75     size_t privateIndex = localeString.find("-x-");
76     if (privateIndex != std::string::npos && privateIndex < foundExtension) {
77         parsedResult.base = localeString;
78         return parsedResult;
79     }
80     const std::string basis = localeString.substr(0, foundExtension);
81     size_t extensionEnd = len;
82     ASSERT(len > INTL_INDEX_TWO);
83     size_t start = foundExtension + 1;
84     HandleLocaleExtension(start, extensionEnd, localeString, len);
85     const std::string end = localeString.substr(extensionEnd);
86     parsedResult.base = basis + end;
87     parsedResult.extension = localeString.substr(foundExtension, extensionEnd - foundExtension);
88     return parsedResult;
89 }
90 
HandleLocaleExtension(size_t & start,size_t & extensionEnd,const std::string & result,size_t len)91 void HandleLocaleExtension(size_t &start, size_t &extensionEnd, const std::string &result, size_t len)
92 {
93     while (start < len - INTL_INDEX_TWO) {
94         if (result[start] != '-') {
95             start++;
96             continue;
97         }
98         if (result[start + INTL_INDEX_TWO] == '-') {
99             extensionEnd = start;
100             break;
101         }
102         start += INTL_INDEX_THREE;
103     }
104 }
105 
StdCoreIntlLocaleFillCheckInfo()106 void StdCoreIntlLocaleFillCheckInfo()
107 {
108     int32_t availableCount;
109     const icu::Locale *availableLocales = icu::Locale::getAvailableLocales(availableCount);
110 
111     for (int i = 0; i < availableCount; ++i) {
112         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
113         const auto &locale = availableLocales[i];
114         const std::string lang = locale.getLanguage();
115         const std::string region = locale.getCountry();
116         const std::string script = locale.getScript();
117         if (!lang.empty()) {
118             GetLocaleCheckInfo().langs.insert(lang);
119         }
120         if (!region.empty()) {
121             GetLocaleCheckInfo().regions.insert(region);
122         }
123         if (!script.empty()) {
124             GetLocaleCheckInfo().scripts.insert(script);
125         }
126     }
127 }
128 
Set2String(std::set<std::string> & inSet)129 std::string Set2String(std::set<std::string> &inSet)
130 {
131     const int lastCommaLength = 1;
132     std::string outList;
133     for (const auto &item : inSet) {
134         outList += item;
135         outList += ",";
136     }
137     if (outList.length() > lastCommaLength) {
138         outList.erase(outList.length() - lastCommaLength);
139     }
140     return outList;
141 }
142 
StdCoreIntlLocaleDefaultLang(ani_env * env,ani_class klass)143 ani_string StdCoreIntlLocaleDefaultLang(ani_env *env, [[maybe_unused]] ani_class klass)
144 {
145     const std::string lang = icu::Locale::getDefault().getLanguage();
146     return StdStrToAni(env, lang);
147 }
148 
StdCoreIntlLocaleRegionList(ani_env * env,ani_class klass)149 ani_string StdCoreIntlLocaleRegionList(ani_env *env, [[maybe_unused]] ani_class klass)
150 {
151     std::string regionsList = Set2String(GetLocaleCheckInfo().regions);
152     return StdStrToAni(env, regionsList);
153 }
154 
StdCoreIntlLocaleLangList(ani_env * env,ani_class klass)155 ani_string StdCoreIntlLocaleLangList(ani_env *env, [[maybe_unused]] ani_class klass)
156 {
157     std::string langsList = Set2String(GetLocaleCheckInfo().langs);
158     return StdStrToAni(env, langsList);
159 }
160 
StdCoreIntlLocaleScriptList(ani_env * env,ani_class klass)161 ani_string StdCoreIntlLocaleScriptList(ani_env *env, [[maybe_unused]] ani_class klass)
162 {
163     std::string scriptList = Set2String(GetLocaleCheckInfo().scripts);
164     return StdStrToAni(env, scriptList);
165 }
166 
StdCoreIntlLocaleNumberingSystemList(ani_env * env,ani_class klass)167 ani_string StdCoreIntlLocaleNumberingSystemList(ani_env *env, [[maybe_unused]] ani_class klass)
168 {
169     UErrorCode success = U_ZERO_ERROR;
170     std::unique_ptr<icu::StringEnumeration> numberingSystemList(icu::NumberingSystem::getAvailableNames(success));
171     if (UNLIKELY(U_FAILURE(success))) {
172         ASSERT(true);
173     }
174     std::string nsList;
175     while (const icu::UnicodeString *ns = numberingSystemList->snext(success)) {
176         if (UNLIKELY(U_FAILURE(success))) {
177             ASSERT(true);
178         }
179         std::string str;
180         ns->toUTF8String(str);
181         nsList += str;
182     }
183 
184     return StdStrToAni(env, nsList);
185 }
186 
StdCoreIntlLocaleInfo(ani_env * env,ani_class klass,ani_string lang)187 ani_string StdCoreIntlLocaleInfo(ani_env *env, [[maybe_unused]] ani_class klass, ani_string lang)
188 {
189     std::string langKey = ConvertFromAniString(env, lang);
190 
191     UErrorCode success = U_ZERO_ERROR;
192     auto l = icu::Locale::forLanguageTag(langKey, success);
193     if (UNLIKELY(U_FAILURE(success))) {
194         ASSERT(true);
195     }
196     l.addLikelySubtags(success);
197     if (UNLIKELY(U_FAILURE(success))) {
198         ASSERT(true);
199     }
200 
201     std::string result;
202     result += l.getCountry();
203     result += ",";
204     result += l.getScript();
205 
206     return StdStrToAni(env, result);
207 }
208 
StdCoreIntlLocaleIsTagValid(ani_env * env,ani_class klass,ani_string tag)209 ani_int StdCoreIntlLocaleIsTagValid(ani_env *env, [[maybe_unused]] ani_class klass, ani_string tag)
210 {
211     std::string tagKey = ConvertFromAniString(env, tag);
212 
213     UErrorCode success = U_ZERO_ERROR;
214     icu::Locale::forLanguageTag(tagKey, success);
215     if (UNLIKELY(U_FAILURE(success))) {
216         return 1;
217     }
218     return 0;
219 }
220 
UnicodeKeyToLocaleInfo(const std::string & unicodeKey)221 static int UnicodeKeyToLocaleInfo(const std::string &unicodeKey)
222 {
223     int keyIndex = -1;
224     if (unicodeKey == "ca") {
225         keyIndex = LocaleInfo::U_CA;
226     } else if (unicodeKey == "co") {
227         keyIndex = LocaleInfo::U_CO;
228     } else if (unicodeKey == "kf") {
229         keyIndex = LocaleInfo::U_KF;
230     } else if (unicodeKey == "hc") {
231         keyIndex = LocaleInfo::U_HC;
232     } else if (unicodeKey == "nu") {
233         keyIndex = LocaleInfo::U_NU;
234     } else if (unicodeKey == "kn") {
235         keyIndex = LocaleInfo::U_KN;
236     }
237     return keyIndex;
238 }
239 
StdCoreIntlLocaleParseTag(ani_env * env,ani_class klass,ani_string tag)240 ani_ref StdCoreIntlLocaleParseTag(ani_env *env, [[maybe_unused]] ani_class klass, ani_string tag)
241 {
242     std::vector<std::string> data = GetLocaleInfo();
243     std::string tagKey = ConvertFromAniString(env, tag);
244     UErrorCode success = U_ZERO_ERROR;
245 
246     auto l = icu::Locale::forLanguageTag(tagKey, success);
247     if (UNLIKELY(U_FAILURE(success))) {
248         std::string message = "Failed to find locale from tag";
249         ThrowNewError(env, "Lstd/core/RuntimeException;", message.c_str(), "Lstd/core/String;:V");
250         return nullptr;
251     }
252 
253     data[LocaleInfo::LANG] = l.getLanguage();
254     data[LocaleInfo::SCRIPT] = l.getScript();
255     data[LocaleInfo::COUNTRY] = l.getCountry();
256 
257     std::set<std::string> keyWordsSet;
258     l.getUnicodeKeywords<std::string>(std::insert_iterator<decltype(keyWordsSet)>(keyWordsSet, keyWordsSet.begin()),
259                                       success);
260 
261     for (auto &unicodeKey : keyWordsSet) {
262         auto unicodeKeywordValue = l.getUnicodeKeywordValue<std::string>(unicodeKey, success);
263         if (UNLIKELY(U_FAILURE(success))) {
264             std::string message = "Failed to getUnicodeKeywordValue";
265             ThrowNewError(env, "Lstd/core/RuntimeException;", message.c_str(), "Lstd/core/String;:V");
266             return nullptr;
267         }
268         int keyIndex = UnicodeKeyToLocaleInfo(unicodeKey);
269         if (keyIndex >= LocaleInfo::U_CA && keyIndex < LocaleInfo::COUNT) {
270             data[keyIndex] = unicodeKeywordValue;
271         }
272     }
273 
274     std::stringstream s;
275     for (int i = 0; i < LocaleInfo::COUNT; ++i) {
276         s << (data[i]) << " ";
277     }
278     std::string res = s.str();
279     if (res.length() > 1) {
280         res.resize(res.length() - 1);
281     }
282 
283     return StdStrToAni(env, res);
284 }
285 
StdCoreIntlLocaleDefaultTag(ani_env * env)286 ani_string StdCoreIntlLocaleDefaultTag(ani_env *env)
287 {
288     auto defaultLocale = icu::Locale::getDefault();
289     auto defaultLocaleName = defaultLocale.getName();
290     if (strcmp(defaultLocaleName, "en_US_POSIX") == 0 || strcmp(defaultLocaleName, "c") == 0) {
291         return StdStrToAni(env, "en-US");
292     }
293     auto status = UErrorCode::U_ZERO_ERROR;
294     auto tag = defaultLocale.toLanguageTag<std::string>(status);
295     if (UNLIKELY(U_FAILURE(status))) {
296         std::string message = "Error receiving default locale language tag: ";
297         message += u_errorName(status);
298         ThrowNewError(env, "Lstd/core/RuntimeException;", message.c_str(), "Lstd/core/String;:V");
299         return nullptr;
300     }
301     return StdStrToAni(env, tag);
302 }
303 
RegisterIntlLocaleNativeMethods(ani_env * env)304 ani_status RegisterIntlLocaleNativeMethods(ani_env *env)
305 {
306     const auto methods = std::array {
307         ani_native_function {"initLocale", ":V", reinterpret_cast<void *>(StdCoreIntlLocaleFillCheckInfo)},
308         ani_native_function {"maximizeInfo", "Lstd/core/String;:Lstd/core/String;",
309                              reinterpret_cast<void *>(StdCoreIntlLocaleInfo)},
310         ani_native_function {"regionList", ":Lstd/core/String;", reinterpret_cast<void *>(StdCoreIntlLocaleRegionList)},
311         ani_native_function {"langList", ":Lstd/core/String;", reinterpret_cast<void *>(StdCoreIntlLocaleLangList)},
312         ani_native_function {"scriptList", ":Lstd/core/String;", reinterpret_cast<void *>(StdCoreIntlLocaleScriptList)},
313         ani_native_function {"numberingSystemList", ":Lstd/core/String;",
314                              reinterpret_cast<void *>(StdCoreIntlLocaleNumberingSystemList)},
315         ani_native_function {"defaultLang", ":Lstd/core/String;",
316                              reinterpret_cast<void *>(StdCoreIntlLocaleDefaultLang)},
317         ani_native_function {"isTagValid", "Lstd/core/String;:I",
318                              reinterpret_cast<void *>(StdCoreIntlLocaleIsTagValid)},
319         ani_native_function {"parseTagImpl", "Lstd/core/String;:Lstd/core/String;",
320                              reinterpret_cast<void *>(StdCoreIntlLocaleParseTag)},
321         ani_native_function {"defaultTag", ":Lstd/core/String;",
322                              reinterpret_cast<void *>(StdCoreIntlLocaleDefaultTag)}};
323 
324     ani_class localeClass;
325     ANI_FATAL_IF_ERROR(env->FindClass("Lstd/core/Intl/Locale;", &localeClass));
326     return env->Class_BindNativeMethods(localeClass, methods.data(), methods.size());
327 }
328 
StdCoreIntlLocaleDefaultBaseName(ani_env * env,ani_class klass)329 ani_string StdCoreIntlLocaleDefaultBaseName(ani_env *env, [[maybe_unused]] ani_class klass)
330 {
331     const std::string name = icu::Locale::getDefault().getBaseName();
332     return StdStrToAni(env, name);
333 }
334 
335 }  // namespace ark::ets::stdlib::intl
336