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
16 #include "intl_addon.h"
17
18 #include "i18n_hilog.h"
19 #include "number_format_addon.h"
20 #include "variable_converter.h"
21
22 using namespace OHOS;
23 using namespace Global;
24 using namespace I18n;
25
UnwrapAddon(ani_env * env,ani_object object,const std::string & name)26 static IntlAddon* UnwrapAddon(ani_env *env, ani_object object, const std::string &name)
27 {
28 ani_long ptr;
29 if (ANI_OK != env->Object_GetFieldByName_Long(object, name.c_str(), &ptr)) {
30 HILOG_ERROR_I18N("Get Long '%{public}s' failed", name.c_str());
31 return nullptr;
32 }
33 return reinterpret_cast<IntlAddon*>(ptr);
34 }
35
DateTimeFormat_Format(ani_env * env,ani_object object,ani_object date)36 ani_string IntlAddon::DateTimeFormat_Format(ani_env *env, ani_object object, ani_object date)
37 {
38 static const char *className = "Lescompat/Date;";
39 ani_class cls;
40 if (ANI_OK != env->FindClass(className, &cls)) {
41 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
42 return nullptr;
43 }
44
45 ani_method getms;
46 if (ANI_OK != env->Class_FindMethod(cls, "valueOf", nullptr, &getms)) {
47 HILOG_ERROR_I18N("Find method 'valueOf' failed");
48 return nullptr;
49 }
50
51 ani_double milliseconds;
52 if (ANI_OK != env->Object_CallMethod_Double(date, getms, &milliseconds)) {
53 HILOG_ERROR_I18N("Call method 'valueOf' failed");
54 return nullptr;
55 }
56
57 IntlAddon *obj = UnwrapAddon(env, object, "nativeDateTimeFormat");
58 if (obj == nullptr || obj->datefmt_ == nullptr) {
59 HILOG_ERROR_I18N("FormatDateTime: Get DateTimeFormat object failed");
60 return nullptr;
61 }
62
63 std::string str = obj->datefmt_->Format(milliseconds);
64 return VariableConverter::StringToAniStr(env, str);
65 }
66
DateTimeFormat_FormatRange(ani_env * env,ani_object object,ani_object startDate,ani_object endDate)67 ani_string IntlAddon::DateTimeFormat_FormatRange(ani_env *env, ani_object object,
68 ani_object startDate, ani_object endDate)
69 {
70 static const char *className = "Lescompat/Date;";
71 ani_class cls;
72 if (ANI_OK != env->FindClass(className, &cls)) {
73 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
74 return nullptr;
75 }
76
77 ani_method getms;
78 if (ANI_OK != env->Class_FindMethod(cls, "valueOf", nullptr, &getms)) {
79 HILOG_ERROR_I18N("Find method 'valueOf' failed");
80 return nullptr;
81 }
82
83 ani_double firstMilliseconds;
84 if (ANI_OK != env->Object_CallMethod_Double(startDate, getms, &firstMilliseconds)) {
85 HILOG_ERROR_I18N("Call method 'valueOf' failed");
86 return nullptr;
87 }
88
89 ani_double secondMilliseconds;
90 if (ANI_OK != env->Object_CallMethod_Double(endDate, getms, &secondMilliseconds)) {
91 HILOG_ERROR_I18N("Call method 'valueOf' failed");
92 return nullptr;
93 }
94
95 IntlAddon *obj = UnwrapAddon(env, object, "nativeDateTimeFormat");
96 if (obj == nullptr || obj->datefmt_ == nullptr) {
97 HILOG_ERROR_I18N("FormatDateTimeRange: Get DateTimeFormat object failed");
98 return nullptr;
99 }
100
101 std::string str = obj->datefmt_->FormatRange(firstMilliseconds, secondMilliseconds);
102 return VariableConverter::StringToAniStr(env, str);
103 }
104
DateTimeFormat_ResolvedOptions(ani_env * env,ani_object object)105 ani_object IntlAddon::DateTimeFormat_ResolvedOptions(ani_env *env, ani_object object)
106 {
107 IntlAddon *obj = UnwrapAddon(env, object, "nativeDateTimeFormat");
108 if (obj == nullptr || obj->datefmt_ == nullptr) {
109 HILOG_ERROR_I18N("GetDateTimeResolvedOptions: Get DateTimeFormat object failed");
110 return nullptr;
111 }
112 std::map<std::string, std::string> options = {};
113 obj->datefmt_->GetResolvedOptions(options);
114
115 static const char *className = "L@ohos/intl/intl/DateTimeOptionsInner;";
116 ani_class cls;
117 if (ANI_OK != env->FindClass(className, &cls)) {
118 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
119 return nullptr;
120 }
121
122 ani_method ctor;
123 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
124 HILOG_ERROR_I18N("Find method '<ctor>' failed");
125 return nullptr;
126 }
127
128 ani_object dateTimeOptions;
129 if (ANI_OK != env->Object_New(cls, ctor, &dateTimeOptions)) {
130 HILOG_ERROR_I18N("New object '%{public}s' failed", className);
131 return nullptr;
132 }
133
134 std::vector<std::string> keys = {
135 "locale", "dateStyle", "timeStyle", "hourCycle", "timeZone", "numberingSystem",
136 "weekday", "era", "year", "month", "day", "hour", "minute", "second",
137 "timeZoneName", "dayPeriod", "localeMatcher", "formatMatcher"
138 };
139
140 for (const auto& key : keys) {
141 if (options.find(key) != options.end()) {
142 VariableConverter::SetStringMember(env, dateTimeOptions, key, options[key]);
143 }
144 }
145 VariableConverter::SetBooleanMember(env, dateTimeOptions, "hour12", options["hour12"]);
146 return dateTimeOptions;
147 }
148
149
Collator_Compare(ani_env * env,ani_object object,ani_string first,ani_string second)150 ani_double IntlAddon::Collator_Compare(ani_env *env, ani_object object, ani_string first, ani_string second)
151 {
152 IntlAddon *obj = UnwrapAddon(env, object, "nativeCollator");
153 if (obj == nullptr || obj->collator_ == nullptr) {
154 HILOG_ERROR_I18N("CompareString: Get Collator object failed");
155 return -1;
156 }
157
158 CompareResult compareResult = obj->collator_->Compare(
159 VariableConverter::AniStrToString(env, first), VariableConverter::AniStrToString(env, second));
160 return static_cast<double>(compareResult);
161 }
162
Collator_ResolvedOptions(ani_env * env,ani_object object)163 ani_object IntlAddon::Collator_ResolvedOptions(ani_env *env, ani_object object)
164 {
165 IntlAddon *obj = UnwrapAddon(env, object, "nativeCollator");
166 if (obj == nullptr || obj->collator_ == nullptr) {
167 HILOG_ERROR_I18N("GetCollatorResolvedOptions: Get Collator object failed");
168 return nullptr;
169 }
170 std::map<std::string, std::string> options = {};
171 obj->collator_->ResolvedOptions(options);
172
173 static const char *className = "L@ohos/intl/intl/CollatorOptionsInner;";
174 ani_class cls;
175 if (ANI_OK != env->FindClass(className, &cls)) {
176 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
177 return nullptr;
178 }
179
180 ani_method ctor;
181 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
182 HILOG_ERROR_I18N("Find method '<ctor>' failed");
183 return nullptr;
184 }
185
186 ani_object collatorOptions;
187 if (ANI_OK != env->Object_New(cls, ctor, &collatorOptions)) {
188 HILOG_ERROR_I18N("New object '%{public}s' failed", className);
189 return nullptr;
190 }
191
192 std::vector<std::string> keys = {"localeMatcher", "usage", "sensitivity", "collation", "caseFirst"};
193 for (const auto& key : keys) {
194 if (options.find(key) != options.end()) {
195 VariableConverter::SetStringMember(env, collatorOptions, key, options[key]);
196 }
197 }
198 VariableConverter::SetBooleanMember(env, collatorOptions, "ignorePunctuation", options["ignorePunctuation"]);
199 VariableConverter::SetBooleanMember(env, collatorOptions, "numeric", options["numeric"]);
200 return collatorOptions;
201 }
202
DateTimeFormat_Create(ani_env * env,ani_object object)203 ani_object IntlAddon::DateTimeFormat_Create(ani_env *env, [[maybe_unused]] ani_object object)
204 {
205 std::unique_ptr<IntlAddon> obj = std::make_unique<IntlAddon>();
206 std::vector<std::string> localeTags;
207 std::map<std::string, std::string> map = {};
208
209 obj->datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
210 static const char *className = "L@ohos/intl/intl/DateTimeFormat;";
211 ani_object dateTimeFormatObject = VariableConverter::CreateAniObject(env, className, obj.get());
212 obj.release();
213 return dateTimeFormatObject;
214 }
215
GetDateTimeFormatOptions(ani_env * env,ani_object options)216 static std::map<std::string, std::string> GetDateTimeFormatOptions(ani_env *env, ani_object options)
217 {
218 std::map<std::string, std::string> map;
219 ani_boolean isUndefined;
220 if (ANI_OK != env->Reference_IsUndefined(options, &isUndefined)) {
221 HILOG_ERROR_I18N("Reference IsUndefined failed");
222 return map;
223 }
224 if (!isUndefined) {
225 std::vector<std::string> stringKeys = {
226 "locale", "dateStyle", "timeStyle", "hourCycle", "timeZone", "numberingSystem",
227 "weekday", "era", "year", "month", "day", "hour", "minute", "second",
228 "timeZoneName", "dayPeriod", "localeMatcher", "formatMatcher"
229 };
230 for (const auto& key : stringKeys) {
231 std::string value;
232 if (VariableConverter::GetStringMember(env, options, key, value)) {
233 map.insert(std::make_pair(key, value));
234 }
235 }
236
237 bool hour12;
238 if (VariableConverter::GetBooleanMember(env, options, "hour12", hour12)) {
239 map.insert(std::make_pair("hour12", hour12 ? "true" : "false"));
240 }
241 }
242 return map;
243 }
244
DateTimeFormat_CreateWithParam(ani_env * env,ani_object object,ani_object locale,ani_object options)245 ani_object IntlAddon::DateTimeFormat_CreateWithParam(ani_env *env, [[maybe_unused]] ani_object object,
246 ani_object locale, ani_object options)
247 {
248 std::vector<std::string> localeTags = VariableConverter::GetLocaleTags(env, locale);
249 std::map<std::string, std::string> map = GetDateTimeFormatOptions(env, options);
250
251 std::unique_ptr<IntlAddon> obj = std::make_unique<IntlAddon>();
252 obj->datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
253
254 static const char *className = "L@ohos/intl/intl/DateTimeFormat;";
255 ani_object dateTimeFormatObject = VariableConverter::CreateAniObject(env, className, obj.get());
256 obj.release();
257 return dateTimeFormatObject;
258 }
259
Collator_Create(ani_env * env,ani_object object)260 ani_object IntlAddon::Collator_Create(ani_env *env, [[maybe_unused]] ani_object object)
261 {
262 std::vector<std::string> localeTags;
263 std::map<std::string, std::string> map = {};
264 std::unique_ptr<IntlAddon> obj = std::make_unique<IntlAddon>();
265 obj->collator_ = std::make_unique<Collator>(localeTags, map);
266
267 static const char *className = "L@ohos/intl/intl/Collator;";
268 ani_object collatorObject = VariableConverter::CreateAniObject(env, className, obj.get());
269 obj.release();
270 return collatorObject;
271 }
272
GetCollatorOptions(ani_env * env,ani_object options)273 static std::map<std::string, std::string> GetCollatorOptions(ani_env *env, ani_object options)
274 {
275 std::map<std::string, std::string> map;
276 ani_boolean isUndefined;
277 if (ANI_OK != env->Reference_IsUndefined(options, &isUndefined)) {
278 HILOG_ERROR_I18N("Reference IsUndefined failed");
279 return map;
280 }
281 if (!isUndefined) {
282 std::vector<std::string> stringKeys = {"localeMatcher", "usage", "sensitivity", "collation", "caseFirst"};
283 for (const auto& key : stringKeys) {
284 std::string value;
285 if (VariableConverter::GetStringMember(env, options, key, value)) {
286 map.insert(std::make_pair(key, value));
287 }
288 }
289
290 bool ignorePunctuation;
291 if (VariableConverter::GetBooleanMember(env, options, "ignorePunctuation", ignorePunctuation)) {
292 map.insert(std::make_pair("ignorePunctuation", ignorePunctuation ? "true" : "false"));
293 }
294
295 bool numeric;
296 if (VariableConverter::GetBooleanMember(env, options, "numeric", numeric)) {
297 map.insert(std::make_pair("numeric", numeric ? "true" : "false"));
298 }
299 }
300 return map;
301 }
302
Collator_CreateWithParam(ani_env * env,ani_object object,ani_object locale,ani_object options)303 ani_object IntlAddon::Collator_CreateWithParam(ani_env *env, [[maybe_unused]] ani_object object,
304 ani_object locale, ani_object options)
305 {
306 std::vector<std::string> localeTags = VariableConverter::GetLocaleTags(env, locale);
307 std::map<std::string, std::string> map = GetCollatorOptions(env, options);
308
309 std::unique_ptr<IntlAddon> obj = std::make_unique<IntlAddon>();
310 obj->collator_ = std::make_unique<Collator>(localeTags, map);
311
312 static const char *className = "L@ohos/intl/intl/Collator;";
313 ani_object collatorObject = VariableConverter::CreateAniObject(env, className, obj.get());
314 obj.release();
315 return collatorObject;
316 }
317
BindContext_DateTimeFormat(ani_env * env)318 ani_status IntlAddon::BindContext_DateTimeFormat(ani_env *env)
319 {
320 static const char *className = "L@ohos/intl/intl/DateTimeFormat;";
321 ani_class cls;
322 if (ANI_OK != env->FindClass(className, &cls)) {
323 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
324 return ANI_ERROR;
325 }
326
327 std::array methods = {
328 ani_native_function {"create", ":L@ohos/intl/intl/DateTimeFormat;",
329 reinterpret_cast<void *>(DateTimeFormat_Create) },
330 ani_native_function {"create",
331 "Lstd/core/Object;L@ohos/intl/intl/DateTimeOptions;:L@ohos/intl/intl/DateTimeFormat;",
332 reinterpret_cast<void *>(DateTimeFormat_CreateWithParam) },
333 ani_native_function {"format", nullptr, reinterpret_cast<void *>(DateTimeFormat_Format) },
334 ani_native_function {"formatRange", nullptr, reinterpret_cast<void *>(DateTimeFormat_FormatRange) },
335 ani_native_function {"resolvedOptions", nullptr, reinterpret_cast<void *>(DateTimeFormat_ResolvedOptions) },
336 };
337
338 if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
339 HILOG_ERROR_I18N("Cannot bind native methods to '%{public}s'", className);
340 return ANI_ERROR;
341 };
342 return ANI_OK;
343 }
344
BindContext_Collator(ani_env * env)345 ani_status IntlAddon::BindContext_Collator(ani_env *env)
346 {
347 static const char *className = "L@ohos/intl/intl/Collator;";
348 ani_class cls;
349 if (ANI_OK != env->FindClass(className, &cls)) {
350 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
351 return ANI_ERROR;
352 }
353
354 std::array methods = {
355 ani_native_function {"create", ":L@ohos/intl/intl/Collator;", reinterpret_cast<void *>(Collator_Create) },
356 ani_native_function {"create",
357 "Lstd/core/Object;L@ohos/intl/intl/CollatorOptions;:L@ohos/intl/intl/Collator;",
358 reinterpret_cast<void *>(Collator_CreateWithParam) },
359 ani_native_function {"compare", nullptr, reinterpret_cast<void *>(Collator_Compare) },
360 ani_native_function {"resolvedOptions", nullptr, reinterpret_cast<void *>(Collator_ResolvedOptions) },
361 };
362
363 if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
364 HILOG_ERROR_I18N("Cannot bind native methods to '%{public}s'", className);
365 return ANI_ERROR;
366 };
367 return ANI_OK;
368 }
369
ANI_Constructor(ani_vm * vm,uint32_t * result)370 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
371 {
372 ani_env *env;
373 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
374 HILOG_ERROR_I18N("Unsupported ANI_VERSION_1");
375 return ANI_ERROR;
376 }
377
378 auto status = IntlAddon::BindContext_DateTimeFormat(env);
379 if (status != ANI_OK) {
380 return status;
381 }
382
383 status = NumberFormatAddon::BindContext_NumberFormat(env);
384 if (status != ANI_OK) {
385 return status;
386 }
387
388 status = IntlAddon::BindContext_Collator(env);
389 if (status != ANI_OK) {
390 return status;
391 }
392
393 *result = ANI_VERSION_1;
394 return ANI_OK;
395 }
396