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 "collator_addon.h"
16
17 #include "error_util.h"
18 #include "intl_addon.h"
19 #include "i18n_hilog.h"
20 #include "js_utils.h"
21
22 namespace OHOS {
23 namespace Global {
24 namespace I18n {
25 static thread_local napi_ref g_constructor = nullptr;
26
CollatorAddon()27 CollatorAddon::CollatorAddon() : env_(nullptr) {}
28
~CollatorAddon()29 CollatorAddon::~CollatorAddon()
30 {
31 }
32
Destructor(napi_env env,void * nativeObject,void * hint)33 void CollatorAddon::Destructor(napi_env env, void *nativeObject, void *hint)
34 {
35 if (!nativeObject) {
36 return;
37 }
38 delete reinterpret_cast<CollatorAddon *>(nativeObject);
39 nativeObject = nullptr;
40 }
41
InitCollator(napi_env env,napi_value exports)42 napi_value CollatorAddon::InitCollator(napi_env env, napi_value exports)
43 {
44 napi_property_descriptor properties[] = {
45 DECLARE_NAPI_FUNCTION("compare", CompareString),
46 DECLARE_NAPI_FUNCTION("resolvedOptions", GetCollatorResolvedOptions),
47 DECLARE_NAPI_STATIC_FUNCTION("supportedLocalesOf", SupportedLocalesOf),
48 };
49
50 napi_value constructor;
51 napi_status status = napi_define_class(env, "Collator", NAPI_AUTO_LENGTH, CollatorConstructor, nullptr,
52 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
53 if (status != napi_ok) {
54 HILOG_ERROR_I18N("Define class failed when InitCollator");
55 return nullptr;
56 }
57
58 status = napi_set_named_property(env, exports, "Collator", constructor);
59 if (status != napi_ok) {
60 HILOG_ERROR_I18N("Set property failed when InitCollator");
61 return nullptr;
62 }
63
64 status = napi_create_reference(env, constructor, 1, &g_constructor);
65 if (status != napi_ok || g_constructor == nullptr) {
66 HILOG_ERROR_I18N("InitCollator: napi_create_reference failed");
67 return nullptr;
68 }
69
70 return exports;
71 }
72
CollatorConstructor(napi_env env,napi_callback_info info)73 napi_value CollatorAddon::CollatorConstructor(napi_env env, napi_callback_info info)
74 {
75 size_t argc = 2;
76 napi_value argv[2] = { nullptr };
77 napi_value thisVar = nullptr;
78 void *data = nullptr;
79 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
80 if (status != napi_ok) {
81 HILOG_ERROR_I18N("CollatorConstructor: napi_get_cb_info failed");
82 return nullptr;
83 }
84
85 napi_value new_target;
86 status = napi_get_new_target(env, info, &new_target);
87 if (status == napi_pending_exception || new_target == nullptr) {
88 return CreateCollatorWithoutNew(env, argc, argv);
89 }
90
91 std::vector<std::string> localeTags;
92 if (argc > 0) {
93 int32_t code = 0;
94 std::vector<std::string> localeArray = JSUtils::GetLocaleArray(env, argv[0], code);
95 localeTags.assign(localeArray.begin(), localeArray.end());
96 }
97 std::map<std::string, std::string> map = {};
98 if (argc > 1) {
99 GetCollatorOptionValue(env, argv[1], map);
100 }
101 CollatorAddon* obj = new(std::nothrow) CollatorAddon();
102 if (obj == nullptr) {
103 HILOG_ERROR_I18N("CollatorConstructor: make_unique CollatorAddon failed");
104 return nullptr;
105 }
106 status =
107 napi_wrap(env, thisVar, reinterpret_cast<void *>(obj), CollatorAddon::Destructor, nullptr, nullptr);
108 if (status != napi_ok) {
109 HILOG_ERROR_I18N("CollatorConstructor: Wrap CollatorAddon failed");
110 delete obj;
111 return nullptr;
112 }
113 if (!obj->InitCollatorContext(env, info, localeTags, map)) {
114 HILOG_ERROR_I18N("CollatorConstructor: Init CollatorAddon failed");
115 return nullptr;
116 }
117 return thisVar;
118 }
119
CreateCollatorWithoutNew(napi_env env,size_t argc,napi_value * argv)120 napi_value CollatorAddon::CreateCollatorWithoutNew(napi_env env, size_t argc, napi_value *argv)
121 {
122 napi_value exception;
123 napi_status status = napi_get_and_clear_last_exception(env, &exception);
124 if (status != napi_ok) {
125 HILOG_ERROR_I18N("CreateCollatorWithoutNew: napi_get_and_clear_last_exception failed");
126 return nullptr;
127 }
128
129 napi_value constructor = nullptr;
130 status = napi_get_reference_value(env, g_constructor, &constructor);
131 if (status != napi_ok || constructor == nullptr) {
132 HILOG_ERROR_I18N("CreateCollatorWithoutNew: napi_get_reference_value failed");
133 return nullptr;
134 }
135
136 napi_value instance = nullptr;
137 status = napi_new_instance(env, constructor, argc, argv, &instance);
138 if (status != napi_ok || instance == nullptr) {
139 HILOG_ERROR_I18N("CreateCollatorWithoutNew: napi_new_instance failed");
140 return nullptr;
141 }
142 return instance;
143 }
144
InitCollatorContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)145 bool CollatorAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
146 std::map<std::string, std::string> &map)
147 {
148 env_ = env;
149 collator_ = std::make_unique<Collator>(localeTags, map, "en-US");
150 if (collator_ == nullptr) {
151 HILOG_ERROR_I18N("CollatorAddon: CollatorConstructor make_unique Collator failed");
152 return false;
153 }
154 I18nErrorCode errorCode = collator_->GetError();
155 if (errorCode == I18nErrorCode::INVALID_PARAM) {
156 HILOG_ERROR_I18N("CollatorAddon: CollatorConstructor invalid param");
157 ErrorUtil::NapiThrowUndefined(env, "getStringOption failed");
158 return false;
159 } else if (errorCode == I18nErrorCode::INVALID_LOCALE_TAG) {
160 HILOG_ERROR_I18N("CollatorAddon: CollatorConstructor invalid locale");
161 ErrorUtil::NapiThrowUndefined(env, "invalid locale");
162 return false;
163 }
164 return errorCode == I18nErrorCode::SUCCESS;
165 }
166
SupportedLocalesOf(napi_env env,napi_callback_info info)167 napi_value CollatorAddon::SupportedLocalesOf(napi_env env, napi_callback_info info)
168 {
169 size_t argc = 2;
170 napi_value argv[2] = { nullptr };
171 napi_value thisVar = nullptr;
172 void *data = nullptr;
173 std::vector<std::string> resultLocales = {};
174 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
175 if (status != napi_ok) {
176 HILOG_ERROR_I18N("SupportedLocalesOf: napi get callback info failed.");
177 return JSUtils::CreateArray(env, resultLocales);
178 }
179 std::vector<std::string> localeTags;
180 if (argc > 0) {
181 int32_t code = 0;
182 std::vector<std::string> localeArray = JSUtils::GetLocaleArray(env, argv[0], code);
183 localeTags.assign(localeArray.begin(), localeArray.end());
184 }
185 std::map<std::string, std::string> map = {};
186 if (argc > 1) {
187 GetCollatorOptionValue(env, argv[1], map);
188 }
189 I18nErrorCode i18nStatus = I18nErrorCode::SUCCESS;
190 resultLocales = Collator::SupportedLocalesOf(localeTags, map, i18nStatus);
191 if (i18nStatus == I18nErrorCode::INVALID_PARAM) {
192 HILOG_ERROR_I18N("SupportedLocalesOf: SupportedLocalesOf invalid param");
193 ErrorUtil::NapiThrowUndefined(env, "getStringOption failed");
194 } else if (i18nStatus == I18nErrorCode::INVALID_LOCALE_TAG) {
195 HILOG_ERROR_I18N("SupportedLocalesOf: SupportedLocalesOf invalid locale");
196 ErrorUtil::NapiThrowUndefined(env, "invalid locale");
197 }
198 return JSUtils::CreateArray(env, resultLocales);
199 }
200
CompareString(napi_env env,napi_callback_info info)201 napi_value CollatorAddon::CompareString(napi_env env, napi_callback_info info)
202 {
203 size_t argc = 2;
204 napi_value argv[2] = { 0 };
205 napi_value thisVar = nullptr;
206 void *data = nullptr;
207 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
208 if (status != napi_ok) {
209 return nullptr;
210 }
211 std::vector<char> first;
212 if (!GetStringParameter(env, argv[0], first)) {
213 return nullptr;
214 }
215
216 std::vector<char> second;
217 if (!GetStringParameter(env, argv[1], second)) {
218 return nullptr;
219 }
220
221 CollatorAddon *obj = nullptr;
222 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
223 if (status != napi_ok || !obj || !obj->collator_) {
224 HILOG_ERROR_I18N("CompareString: Get Collator object failed");
225 return nullptr;
226 }
227
228 CompareResult compareResult = obj->collator_->Compare(first.data(), second.data());
229 napi_value result = nullptr;
230 status = napi_create_int32(env, compareResult, &result);
231 if (status != napi_ok) {
232 HILOG_ERROR_I18N("CompareString: Create compare result failed");
233 return nullptr;
234 }
235
236 return result;
237 }
238
GetCollatorResolvedOptions(napi_env env,napi_callback_info info)239 napi_value CollatorAddon::GetCollatorResolvedOptions(napi_env env, napi_callback_info info)
240 {
241 napi_value thisVar = nullptr;
242 void *data = nullptr;
243 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
244 if (status != napi_ok) {
245 return JSUtils::CreateEmptyObject(env);
246 }
247 CollatorAddon *obj = nullptr;
248 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
249 if (status != napi_ok || !obj || !obj->collator_) {
250 HILOG_ERROR_I18N("GetCollatorResolvedOptions: Get Collator object failed");
251 return JSUtils::CreateEmptyObject(env);
252 }
253 napi_value result = nullptr;
254 status = napi_create_object(env, &result);
255 if (status != napi_ok) {
256 return JSUtils::CreateEmptyObject(env);
257 }
258 std::map<std::string, std::string> options = {};
259 obj->collator_->ResolvedOptions(options);
260 JSUtils::SetOptionProperties(env, result, options, "localeMatcher");
261 JSUtils::SetOptionProperties(env, result, options, "locale");
262 JSUtils::SetOptionProperties(env, result, options, "usage");
263 JSUtils::SetOptionProperties(env, result, options, "sensitivity");
264 JSUtils::SetBooleanOptionProperties(env, result, options, "ignorePunctuation");
265 JSUtils::SetBooleanOptionProperties(env, result, options, "numeric");
266 JSUtils::SetOptionProperties(env, result, options, "caseFirst");
267 JSUtils::SetOptionProperties(env, result, options, "collation");
268 return result;
269 }
270 } // namespace I18n
271 } // namespace Global
272 } // namespace OHOS