• 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 
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