• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 <vector>
19 #include <set>
20 #include "error_util.h"
21 #include "i18n_hilog.h"
22 #include "js_utils.h"
23 #include "node_api.h"
24 #include "utils.h"
25 #include "locale_info_addon.h"
26 #include "number_format_addon.h"
27 
28 namespace OHOS {
29 namespace Global {
30 namespace I18n {
31 
IntlAddon()32 IntlAddon::IntlAddon() : env_(nullptr) {}
33 
~IntlAddon()34 IntlAddon::~IntlAddon()
35 {
36 }
37 
Destructor(napi_env env,void * nativeObject,void * hint)38 void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint)
39 {
40     if (!nativeObject) {
41         return;
42     }
43     delete reinterpret_cast<IntlAddon *>(nativeObject);
44     nativeObject = nullptr;
45 }
46 
InitDateTimeFormat(napi_env env,napi_value exports)47 napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports)
48 {
49     napi_status status = napi_ok;
50     napi_property_descriptor properties[] = {
51         DECLARE_NAPI_FUNCTION("format", FormatDateTime),
52         DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange),
53         DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions)
54     };
55 
56     napi_value constructor = nullptr;
57     status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr,
58         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
59     if (status != napi_ok) {
60         HILOG_ERROR_I18N("Define class failed when InitDateTimeFormat");
61         return nullptr;
62     }
63 
64     status = napi_set_named_property(env, exports, "DateTimeFormat", constructor);
65     if (status != napi_ok) {
66         HILOG_ERROR_I18N("Set property failed when InitDateTimeFormat");
67         return nullptr;
68     }
69     return exports;
70 }
71 
InitRelativeTimeFormat(napi_env env,napi_value exports)72 napi_value IntlAddon::InitRelativeTimeFormat(napi_env env, napi_value exports)
73 {
74     napi_status status = napi_ok;
75     napi_property_descriptor properties[] = {
76         DECLARE_NAPI_FUNCTION("format", FormatRelativeTime),
77         DECLARE_NAPI_FUNCTION("formatToParts", FormatToParts),
78         DECLARE_NAPI_FUNCTION("resolvedOptions", GetRelativeTimeResolvedOptions)
79     };
80 
81     napi_value constructor = nullptr;
82     status = napi_define_class(env, "RelativeTimeFormat", NAPI_AUTO_LENGTH, RelativeTimeFormatConstructor, nullptr,
83         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
84     if (status != napi_ok) {
85         HILOG_ERROR_I18N("Define class failed when InitRelativeTimeFormat");
86         return nullptr;
87     }
88 
89     status = napi_set_named_property(env, exports, "RelativeTimeFormat", constructor);
90     if (status != napi_ok) {
91         HILOG_ERROR_I18N("Set property failed when InitRelativeTimeFormat");
92         return nullptr;
93     }
94     return exports;
95 }
96 
GetDateOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)97 void GetDateOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
98 {
99     JSUtils::GetOptionValue(env, options, "calendar", map);
100     JSUtils::GetOptionValue(env, options, "dateStyle", map);
101     JSUtils::GetOptionValue(env, options, "timeStyle", map);
102     JSUtils::GetOptionValue(env, options, "hourCycle", map);
103     JSUtils::GetOptionValue(env, options, "timeZone", map);
104     JSUtils::GetOptionValue(env, options, "timeZoneName", map);
105     JSUtils::GetOptionValue(env, options, "numberingSystem", map);
106     JSUtils::GetBoolOptionValue(env, options, "hour12", map);
107     JSUtils::GetOptionValue(env, options, "weekday", map);
108     JSUtils::GetOptionValue(env, options, "era", map);
109     JSUtils::GetOptionValue(env, options, "year", map);
110     JSUtils::GetOptionValue(env, options, "month", map);
111     JSUtils::GetOptionValue(env, options, "day", map);
112     JSUtils::GetOptionValue(env, options, "hour", map);
113     JSUtils::GetOptionValue(env, options, "minute", map);
114     JSUtils::GetOptionValue(env, options, "second", map);
115     JSUtils::GetOptionValue(env, options, "localeMatcher", map);
116     JSUtils::GetOptionValue(env, options, "formatMatcher", map);
117     JSUtils::GetOptionValue(env, options, "dayPeriod", map);
118 }
119 
GetRelativeTimeOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)120 void GetRelativeTimeOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
121 {
122     JSUtils::GetOptionValue(env, options, "localeMatcher", map);
123     JSUtils::GetOptionValue(env, options, "numeric", map);
124     JSUtils::GetOptionValue(env, options, "style", map);
125 }
126 
GetLocaleTag(napi_env env,napi_value argv)127 std::string GetLocaleTag(napi_env env, napi_value argv)
128 {
129     std::string localeTag = "";
130     std::vector<char> buf;
131     if (argv != nullptr) {
132         napi_valuetype valueType = napi_valuetype::napi_undefined;
133         napi_typeof(env, argv, &valueType);
134         if (valueType != napi_valuetype::napi_string) {
135             HILOG_ERROR_I18N("GetLocaleTag: Parameter type does not match");
136             return "";
137         }
138         size_t len = 0;
139         napi_status status = napi_get_value_string_utf8(env, argv, nullptr, 0, &len);
140         if (status != napi_ok) {
141             HILOG_ERROR_I18N("GetLocaleTag -> string: Get locale tag length failed");
142             return "";
143         }
144         buf.resize(len + 1);
145         status = napi_get_value_string_utf8(env, argv, buf.data(), len + 1, &len);
146         if (status != napi_ok) {
147             HILOG_ERROR_I18N("GetLocaleTag: Get locale tag failed");
148             return "";
149         }
150         localeTag = buf.data();
151     } else {
152         localeTag = "";
153     }
154     return localeTag;
155 }
156 
DateTimeFormatConstructor(napi_env env,napi_callback_info info)157 napi_value IntlAddon::DateTimeFormatConstructor(napi_env env, napi_callback_info info)
158 {
159     size_t argc = 2;
160     napi_value argv[2] = { nullptr };
161     napi_value thisVar = nullptr;
162     void *data = nullptr;
163     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
164     if (status != napi_ok) {
165         return nullptr;
166     }
167     std::vector<std::string> localeTags;
168     if (argc > 0) {
169         napi_valuetype valueType = napi_valuetype::napi_undefined;
170         napi_typeof(env, argv[0], &valueType);
171         bool isArray = false;
172         napi_is_array(env, argv[0], &isArray);
173         if (valueType == napi_valuetype::napi_string) {
174             JSUtils::GetLocaleTags(env, argv[0], localeTags);
175         } else if (isArray) {
176             uint32_t arrayLength = 0;
177             napi_get_array_length(env, argv[0], &arrayLength);
178             napi_value element = nullptr;
179             for (uint32_t i = 0; i < arrayLength; i++) {
180                 napi_get_element(env, argv[0], i, &element);
181                 JSUtils::GetLocaleTags(env, element, localeTags);
182             }
183         }
184     }
185     std::map<std::string, std::string> map = {};
186     if (argc > 1) {
187         GetDateOptionValues(env, argv[1], map);
188     }
189     std::unique_ptr<IntlAddon> obj = nullptr;
190     obj = std::make_unique<IntlAddon>();
191     status =
192         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
193     if (status != napi_ok) {
194         HILOG_ERROR_I18N("DateTimeFormatConstructor: Wrap IntlAddon failed");
195         return nullptr;
196     }
197     if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) {
198         HILOG_ERROR_I18N("DateTimeFormatConstructor: Init DateTimeFormat failed");
199         return nullptr;
200     }
201     obj.release();
202     return thisVar;
203 }
204 
InitDateTimeFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)205 bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
206     std::map<std::string, std::string> &map)
207 {
208     napi_value global = nullptr;
209     napi_status status = napi_get_global(env, &global);
210     if (status != napi_ok) {
211         HILOG_ERROR_I18N("InitDateTimeFormatContext: Get global failed");
212         return false;
213     }
214     env_ = env;
215     datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
216 
217     return datefmt_ != nullptr;
218 }
219 
RelativeTimeFormatConstructor(napi_env env,napi_callback_info info)220 napi_value IntlAddon::RelativeTimeFormatConstructor(napi_env env, napi_callback_info info)
221 {
222     size_t argc = 2;
223     napi_value argv[2] = { nullptr };
224     napi_value thisVar = nullptr;
225     void *data = nullptr;
226     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
227     if (status != napi_ok) {
228         return nullptr;
229     }
230     std::vector<std::string> localeTags;
231     if (argc > 0) {
232         napi_valuetype valueType = napi_valuetype::napi_undefined;
233         napi_typeof(env, argv[0], &valueType);
234         bool isArray = false;
235         napi_is_array(env, argv[0], &isArray);
236         if (valueType == napi_valuetype::napi_string) {
237             JSUtils::GetLocaleTags(env, argv[0], localeTags);
238         } else if (isArray) {
239             uint32_t arrayLength = 0;
240             napi_get_array_length(env, argv[0], &arrayLength);
241             napi_value element = nullptr;
242             for (uint32_t i = 0; i < arrayLength; i++) {
243                 napi_get_element(env, argv[0], i, &element);
244                 JSUtils::GetLocaleTags(env, element, localeTags);
245             }
246         }
247     }
248     std::map<std::string, std::string> map = {};
249     if (argc > 1) {
250         GetRelativeTimeOptionValues(env, argv[1], map);
251     }
252     std::unique_ptr<IntlAddon> obj = nullptr;
253     obj = std::make_unique<IntlAddon>();
254     status =
255         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
256     if (status != napi_ok) {
257         HILOG_ERROR_I18N("RelativeTimeFormatConstructor: Wrap IntlAddon failed");
258         return nullptr;
259     }
260     if (!obj->InitRelativeTimeFormatContext(env, info, localeTags, map)) {
261         HILOG_ERROR_I18N("Init RelativeTimeFormat failed");
262         return nullptr;
263     }
264     obj.release();
265     return thisVar;
266 }
267 
InitRelativeTimeFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)268 bool IntlAddon::InitRelativeTimeFormatContext(napi_env env, napi_callback_info info,
269     std::vector<std::string> localeTags, std::map<std::string, std::string> &map)
270 {
271     env_ = env;
272     relativetimefmt_ = std::make_unique<RelativeTimeFormat>(localeTags, map);
273 
274     return relativetimefmt_ != nullptr;
275 }
276 
FormatDateTime(napi_env env,napi_callback_info info)277 napi_value IntlAddon::FormatDateTime(napi_env env, napi_callback_info info)
278 {
279     size_t argc = 1;
280     napi_value argv[1] = { 0 };
281     napi_value thisVar = nullptr;
282     void *data = nullptr;
283     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
284 
285     int64_t milliseconds = GetMilliseconds(env, argv, 0);
286     if (milliseconds == -1) {
287         return nullptr;
288     }
289     IntlAddon *obj = nullptr;
290     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
291     if (status != napi_ok || !obj || !obj->datefmt_) {
292         HILOG_ERROR_I18N("FormatDateTime: Get DateTimeFormat object failed");
293         return nullptr;
294     }
295     std::string value = obj->datefmt_->Format(milliseconds);
296     napi_value result = nullptr;
297     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
298     if (status != napi_ok) {
299         HILOG_ERROR_I18N("FormatDateTime: Create format string failed");
300         return nullptr;
301     }
302     return result;
303 }
304 
FormatDateTimeRange(napi_env env,napi_callback_info info)305 napi_value IntlAddon::FormatDateTimeRange(napi_env env, napi_callback_info info)
306 {
307     size_t argc = 2;
308     napi_value argv[2] = { nullptr };
309     napi_value thisVar = nullptr;
310     void *data = nullptr;
311     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
312     if (argc < FUNC_ARGS_COUNT) {
313         HILOG_ERROR_I18N("Parameter wrong");
314         return nullptr;
315     }
316     int64_t firstMilliseconds = GetMilliseconds(env, argv, 0);
317     int64_t secondMilliseconds = GetMilliseconds(env, argv, 1);
318     if (firstMilliseconds == -1 || secondMilliseconds == -1) {
319         return nullptr;
320     }
321     IntlAddon *obj = nullptr;
322     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
323     if (status != napi_ok || !obj || !obj->datefmt_) {
324         HILOG_ERROR_I18N("FormatDateTimeRange: Get DateTimeFormat object failed");
325         return nullptr;
326     }
327     std::string value = obj->datefmt_->FormatRange(firstMilliseconds, secondMilliseconds);
328     napi_value result = nullptr;
329     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
330     if (status != napi_ok) {
331         HILOG_ERROR_I18N("FormatDateTimeRange: Create format string failed");
332         return nullptr;
333     }
334     return result;
335 }
336 
GetMilliseconds(napi_env env,napi_value * argv,int index)337 int64_t IntlAddon::GetMilliseconds(napi_env env, napi_value *argv, int index)
338 {
339     napi_value funcGetDateInfo = nullptr;
340     napi_status status = napi_get_named_property(env, argv[index], "getTime", &funcGetDateInfo);
341     if (status != napi_ok) {
342         HILOG_ERROR_I18N("Get Milliseconds property failed");
343         return -1;
344     }
345     napi_value ret_value = nullptr;
346     status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value);
347     if (status != napi_ok) {
348         HILOG_ERROR_I18N("Get Milliseconds function failed");
349         return -1;
350     }
351     int64_t milliseconds = 0;
352     status = napi_get_value_int64(env, ret_value, &milliseconds);
353     if (status != napi_ok) {
354         HILOG_ERROR_I18N("Get Milliseconds failed");
355         return -1;
356     }
357     return milliseconds;
358 }
359 
GetRelativeTimeResolvedOptions(napi_env env,napi_callback_info info)360 napi_value IntlAddon::GetRelativeTimeResolvedOptions(napi_env env, napi_callback_info info)
361 {
362     napi_value thisVar = nullptr;
363     void *data = nullptr;
364     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
365 
366     IntlAddon *obj = nullptr;
367     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
368     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
369         HILOG_ERROR_I18N("GetRelativeTimeResolvedOptions: Get RelativeTimeFormat object failed");
370         return nullptr;
371     }
372     napi_value result = nullptr;
373     napi_create_object(env, &result);
374     std::map<std::string, std::string> options = {};
375     obj->relativetimefmt_->GetResolvedOptions(options);
376     JSUtils::SetOptionProperties(env, result, options, "locale");
377     JSUtils::SetOptionProperties(env, result, options, "style");
378     JSUtils::SetOptionProperties(env, result, options, "numeric");
379     JSUtils::SetOptionProperties(env, result, options, "numberingSystem");
380     return result;
381 }
382 
GetDateTimeResolvedOptions(napi_env env,napi_callback_info info)383 napi_value IntlAddon::GetDateTimeResolvedOptions(napi_env env, napi_callback_info info)
384 {
385     napi_value thisVar = nullptr;
386     void *data = nullptr;
387     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
388 
389     IntlAddon *obj = nullptr;
390     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
391     if (status != napi_ok || !obj || !obj->datefmt_) {
392         HILOG_ERROR_I18N("GetDateTimeResolvedOptions: Get DateTimeFormat object failed");
393         return nullptr;
394     }
395     napi_value result = nullptr;
396     napi_create_object(env, &result);
397     std::map<std::string, std::string> options = {};
398     obj->datefmt_->GetResolvedOptions(options);
399     JSUtils::SetOptionProperties(env, result, options, "locale");
400     JSUtils::SetOptionProperties(env, result, options, "calendar");
401     JSUtils::SetOptionProperties(env, result, options, "dateStyle");
402     JSUtils::SetOptionProperties(env, result, options, "timeStyle");
403     JSUtils::SetOptionProperties(env, result, options, "hourCycle");
404     JSUtils::SetOptionProperties(env, result, options, "timeZone");
405     JSUtils::SetOptionProperties(env, result, options, "timeZoneName");
406     JSUtils::SetOptionProperties(env, result, options, "numberingSystem");
407     JSUtils::SetBooleanOptionProperties(env, result, options, "hour12");
408     JSUtils::SetOptionProperties(env, result, options, "weekday");
409     JSUtils::SetOptionProperties(env, result, options, "era");
410     JSUtils::SetOptionProperties(env, result, options, "year");
411     JSUtils::SetOptionProperties(env, result, options, "month");
412     JSUtils::SetOptionProperties(env, result, options, "day");
413     JSUtils::SetOptionProperties(env, result, options, "hour");
414     JSUtils::SetOptionProperties(env, result, options, "minute");
415     JSUtils::SetOptionProperties(env, result, options, "second");
416     JSUtils::SetOptionProperties(env, result, options, "dayPeriod");
417     JSUtils::SetOptionProperties(env, result, options, "localeMatcher");
418     JSUtils::SetOptionProperties(env, result, options, "formatMatcher");
419     return result;
420 }
421 
GetCollatorLocaleMatcher(napi_env env,napi_value options,std::map<std::string,std::string> & map)422 void GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map<std::string, std::string> &map)
423 {
424     JSUtils::GetOptionValue(env, options, "localeMatcher", map);
425     auto it = map.find("localeMatcher");
426     if (it != map.end()) {
427         std::string localeMatcher = it->second;
428         if (localeMatcher != "lookup" && localeMatcher != "best fit") {
429             HILOG_ERROR_I18N("invalid localeMatcher");
430             return;
431         }
432     } else {
433         map.insert(std::make_pair("localeMatcher", "best fit"));
434     }
435 }
436 
GetCollatorUsage(napi_env env,napi_value options,std::map<std::string,std::string> & map)437 void GetCollatorUsage(napi_env env, napi_value options, std::map<std::string, std::string> &map)
438 {
439     JSUtils::GetOptionValue(env, options, "usage", map);
440     auto it = map.find("usage");
441     if (it != map.end()) {
442         std::string usage = it->second;
443         if (usage != "sort" && usage != "search") {
444             HILOG_ERROR_I18N("invalid usage");
445             return;
446         }
447     } else {
448         map.insert(std::make_pair("usage", "sort"));
449     }
450 }
451 
GetCollatorSensitivity(napi_env env,napi_value options,std::map<std::string,std::string> & map)452 void GetCollatorSensitivity(napi_env env, napi_value options, std::map<std::string, std::string> &map)
453 {
454     JSUtils::GetOptionValue(env, options, "sensitivity", map);
455     auto it = map.find("sensitivity");
456     if (it != map.end()) {
457         std::string sensitivity = it->second;
458         if (sensitivity != "base" && sensitivity != "accent" && sensitivity != "case" && sensitivity != "variant") {
459             HILOG_ERROR_I18N("invalid sensitivity");
460             return;
461         }
462     } else {
463         map.insert(std::make_pair("sensitivity", "variant"));
464     }
465 }
466 
GetCollatorIgnorePunctuation(napi_env env,napi_value options,std::map<std::string,std::string> & map)467 void GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
468 {
469     JSUtils::GetBoolOptionValue(env, options, "ignorePunctuation", map);
470     auto it = map.find("ignorePunctuation");
471     if (it != map.end()) {
472         std::string ignorePunctuation = it->second;
473         if (ignorePunctuation != "true" && ignorePunctuation != "false") {
474             HILOG_ERROR_I18N("invalid ignorePunctuation");
475             return;
476         }
477     } else {
478         map.insert(std::make_pair("ignorePunctuation", "false"));
479     }
480 }
481 
GetCollatorNumeric(napi_env env,napi_value options,std::map<std::string,std::string> & map)482 void GetCollatorNumeric(napi_env env, napi_value options, std::map<std::string, std::string> &map)
483 {
484     JSUtils::GetBoolOptionValue(env, options, "numeric", map);
485     auto it = map.find("numeric");
486     if (it != map.end()) {
487         std::string numeric = it->second;
488         if (numeric != "true" && numeric != "false") {
489             HILOG_ERROR_I18N("invalid numeric");
490             return;
491         }
492     }
493 }
494 
GetCollatorCaseFirst(napi_env env,napi_value options,std::map<std::string,std::string> & map)495 void GetCollatorCaseFirst(napi_env env, napi_value options, std::map<std::string, std::string> &map)
496 {
497     JSUtils::GetOptionValue(env, options, "caseFirst", map);
498     auto it = map.find("caseFirst");
499     if (it != map.end()) {
500         std::string caseFirst = it->second;
501         if (caseFirst != "upper" && caseFirst != "lower" && caseFirst != "false") {
502             HILOG_ERROR_I18N("invalid caseFirst");
503             return;
504         }
505     }
506 }
507 
GetCollatorCollation(napi_env env,napi_value options,std::map<std::string,std::string> & map)508 void GetCollatorCollation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
509 {
510     JSUtils::GetOptionValue(env, options, "collation", map);
511     auto it = map.find("collation");
512     if (it != map.end()) {
513         std::string collation = it->second;
514         std::set<std::string> validCollation;
515         validCollation.insert("big5han");
516         validCollation.insert("compat");
517         validCollation.insert("dict");
518         validCollation.insert("direct");
519         validCollation.insert("ducet");
520         validCollation.insert("eor");
521         validCollation.insert("gb2312");
522         validCollation.insert("phonebk");
523         validCollation.insert("phonetic");
524         validCollation.insert("pinyin");
525         validCollation.insert("reformed");
526         validCollation.insert("searchjl");
527         validCollation.insert("stroke");
528         validCollation.insert("trad");
529         validCollation.insert("unihan");
530         validCollation.insert("zhuyin");
531         if (validCollation.find(collation) == validCollation.end()) {
532             map["collation"] = "default";
533         }
534     }
535 }
536 
GetCollatorOptionValue(napi_env env,napi_value options,std::map<std::string,std::string> & map)537 void GetCollatorOptionValue(napi_env env, napi_value options, std::map<std::string, std::string> &map)
538 {
539     GetCollatorLocaleMatcher(env, options, map);
540     GetCollatorUsage(env, options, map);
541     GetCollatorSensitivity(env, options, map);
542     GetCollatorIgnorePunctuation(env, options, map);
543     GetCollatorNumeric(env, options, map);
544     GetCollatorCaseFirst(env, options, map);
545     GetCollatorCollation(env, options, map);
546 }
547 
InitCollator(napi_env env,napi_value exports)548 napi_value IntlAddon::InitCollator(napi_env env, napi_value exports)
549 {
550     napi_status status = napi_ok;
551     napi_property_descriptor properties[] = {
552         DECLARE_NAPI_FUNCTION("compare", CompareString),
553         DECLARE_NAPI_FUNCTION("resolvedOptions", GetCollatorResolvedOptions)
554     };
555 
556     napi_value constructor;
557     status = napi_define_class(env, "Collator", NAPI_AUTO_LENGTH, CollatorConstructor, nullptr,
558         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
559     if (status != napi_ok) {
560         HILOG_ERROR_I18N("Define class failed when InitCollator");
561         return nullptr;
562     }
563 
564     status = napi_set_named_property(env, exports, "Collator", constructor);
565     if (status != napi_ok) {
566         HILOG_ERROR_I18N("Set property failed when InitCollator");
567         return nullptr;
568     }
569     return exports;
570 }
571 
CollatorConstructor(napi_env env,napi_callback_info info)572 napi_value IntlAddon::CollatorConstructor(napi_env env, napi_callback_info info)
573 {
574     size_t argc = 2;
575     napi_value argv[2] = { nullptr };
576     napi_value thisVar = nullptr;
577     void *data = nullptr;
578     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
579     if (status != napi_ok) {
580         return nullptr;
581     }
582     std::vector<std::string> localeTags;
583     if (argc > 0) {
584         napi_valuetype valueType = napi_valuetype::napi_undefined;
585         napi_typeof(env, argv[0], &valueType);
586         bool isArray = false;
587         napi_is_array(env, argv[0], &isArray);
588         if (valueType == napi_valuetype::napi_string) {
589             JSUtils::GetLocaleTags(env, argv[0], localeTags);
590         } else if (isArray) {
591             uint32_t arrayLength = 0;
592             napi_get_array_length(env, argv[0], &arrayLength);
593             napi_value element = nullptr;
594             for (uint32_t i = 0; i < arrayLength; i++) {
595                 napi_get_element(env, argv[0], i, &element);
596                 JSUtils::GetLocaleTags(env, element, localeTags);
597             }
598         }
599     }
600     std::map<std::string, std::string> map = {};
601     if (argc > 1) {
602         GetCollatorOptionValue(env, argv[1], map);
603     }
604     std::unique_ptr<IntlAddon> obj = nullptr;
605     obj = std::make_unique<IntlAddon>();
606     status =
607         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
608     if (status != napi_ok) {
609         HILOG_ERROR_I18N("CollatorConstructor: Wrap IntlAddon failed");
610         return nullptr;
611     }
612     if (!obj->InitCollatorContext(env, info, localeTags, map)) {
613         HILOG_ERROR_I18N("CollatorConstructor: Init DateTimeFormat failed");
614         return nullptr;
615     }
616     obj.release();
617     return thisVar;
618 }
619 
InitCollatorContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)620 bool IntlAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
621     std::map<std::string, std::string> &map)
622 {
623     napi_value global = nullptr;
624     napi_status status = napi_get_global(env, &global);
625     if (status != napi_ok) {
626         HILOG_ERROR_I18N("InitCollatorContext: Get global failed");
627         return false;
628     }
629     env_ = env;
630     collator_ = std::make_unique<Collator>(localeTags, map);
631 
632     return collator_ != nullptr;
633 }
634 
GetStringParameter(napi_env env,napi_value value,std::vector<char> & buf)635 bool GetStringParameter(napi_env env, napi_value value, std::vector<char> &buf)
636 {
637     napi_valuetype valueType = napi_valuetype::napi_undefined;
638     napi_typeof(env, value, &valueType);
639     if (valueType != napi_valuetype::napi_string) {
640         HILOG_ERROR_I18N("Parameter type does not match");
641         return false;
642     }
643     size_t len = 0;
644     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
645     if (status != napi_ok) {
646         HILOG_ERROR_I18N("Get first length failed");
647         return false;
648     }
649     buf.resize(len + 1);
650     status = napi_get_value_string_utf8(env, value, buf.data(), len + 1, &len);
651     if (status != napi_ok) {
652         HILOG_ERROR_I18N("Get first failed");
653         return false;
654     }
655 
656     return true;
657 }
658 
FormatRelativeTime(napi_env env,napi_callback_info info)659 napi_value IntlAddon::FormatRelativeTime(napi_env env, napi_callback_info info)
660 {
661     size_t argc = 2;
662     napi_value argv[2] = { 0 };
663     napi_value thisVar = nullptr;
664     void *data = nullptr;
665     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
666     napi_status status;
667     double number;
668     status = napi_get_value_double(env, argv[0], &number);
669     if (status != napi_ok) {
670         HILOG_ERROR_I18N("FormatRelativeTime: Get number failed");
671         return nullptr;
672     }
673     std::vector<char> unit;
674     if (!GetStringParameter(env, argv[1], unit)) {
675         return nullptr;
676     }
677     IntlAddon *obj = nullptr;
678     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
679     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
680         HILOG_ERROR_I18N("FormatRelativeTime: Get RelativeTimeFormat object failed");
681         return nullptr;
682     }
683     std::string value = obj->relativetimefmt_->Format(number, unit.data());
684     napi_value result = nullptr;
685     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
686     if (status != napi_ok) {
687         HILOG_ERROR_I18N("FormatRelativeTime: Create format string failed");
688         return nullptr;
689     }
690     return result;
691 }
692 
FillInArrayElement(napi_env env,napi_value & result,napi_status & status,const std::vector<std::vector<std::string>> & timeVector)693 void IntlAddon::FillInArrayElement(napi_env env, napi_value &result, napi_status &status,
694     const std::vector<std::vector<std::string>> &timeVector)
695 {
696     for (size_t i = 0; i < timeVector.size(); i++) {
697         napi_value value = nullptr;
698         status = napi_create_string_utf8(env, timeVector[i][1].c_str(), NAPI_AUTO_LENGTH, &value);
699         if (status != napi_ok) {
700             HILOG_ERROR_I18N("Failed to create string item imeVector[i][1].");
701             return;
702         }
703         napi_value type = nullptr;
704         status = napi_create_string_utf8(env, timeVector[i][0].c_str(), NAPI_AUTO_LENGTH, &type);
705         if (status != napi_ok) {
706             HILOG_ERROR_I18N("Failed to create string item timeVector[i][0].");
707             return;
708         }
709         napi_value unit = nullptr;
710         size_t unitIndex = 2;
711         if (timeVector[i].size() > unitIndex) {
712             status = napi_create_string_utf8(env, timeVector[i][unitIndex].c_str(), NAPI_AUTO_LENGTH, &unit);
713             if (status != napi_ok) {
714                 HILOG_ERROR_I18N("Failed to create string item timeVector[i][unitIndex].");
715                 return;
716             }
717         } else {
718             napi_get_undefined(env, &unit);
719         }
720         napi_value formatInfo;
721         status = napi_create_object(env, &formatInfo);
722         if (status != napi_ok) {
723             HILOG_ERROR_I18N("Failed to create format info object.");
724             return;
725         }
726         napi_set_named_property(env, formatInfo, "type", type);
727         napi_set_named_property(env, formatInfo, "value", value);
728         napi_set_named_property(env, formatInfo, "unit", unit);
729         status = napi_set_element(env, result, i, formatInfo);
730         if (status != napi_ok) {
731             HILOG_ERROR_I18N("Failed to set array item");
732             return;
733         }
734     }
735 }
736 
FormatToParts(napi_env env,napi_callback_info info)737 napi_value IntlAddon::FormatToParts(napi_env env, napi_callback_info info)
738 {
739     size_t argc = 2;
740     napi_value argv[2] = { 0 };
741     napi_value thisVar = nullptr;
742     void *data = nullptr;
743     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
744     double number = 0;
745     napi_get_value_double(env, argv[0], &number);
746     std::vector<char> unit;
747     if (!GetStringParameter(env, argv[1], unit)) {
748         return nullptr;
749     }
750     IntlAddon *obj = nullptr;
751     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
752     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
753         HILOG_ERROR_I18N("FormatToParts: Get RelativeTimeFormat object failed");
754         return nullptr;
755     }
756     std::vector<std::vector<std::string>> timeVector;
757     obj->relativetimefmt_->FormatToParts(number, unit.data(), timeVector);
758     napi_value result = nullptr;
759     status = napi_create_array_with_length(env, timeVector.size(), &result);
760     if (status != napi_ok) {
761         HILOG_ERROR_I18N("Failed to create array");
762         return nullptr;
763     }
764     FillInArrayElement(env, result, status, timeVector);
765     return result;
766 }
767 
CompareString(napi_env env,napi_callback_info info)768 napi_value IntlAddon::CompareString(napi_env env, napi_callback_info info)
769 {
770     size_t argc = 2;
771     napi_value argv[2] = { 0 };
772     napi_value thisVar = nullptr;
773     void *data = nullptr;
774     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
775 
776     std::vector<char> first;
777     if (!GetStringParameter(env, argv[0], first)) {
778         return nullptr;
779     }
780 
781     std::vector<char> second;
782     if (!GetStringParameter(env, argv[1], second)) {
783         return nullptr;
784     }
785 
786     IntlAddon *obj = nullptr;
787     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
788     if (status != napi_ok || !obj || !obj->collator_) {
789         HILOG_ERROR_I18N("CompareString: Get Collator object failed");
790         return nullptr;
791     }
792 
793     CompareResult compareResult = obj->collator_->Compare(first.data(), second.data());
794     napi_value result = nullptr;
795     status = napi_create_int32(env, compareResult, &result);
796     if (status != napi_ok) {
797         HILOG_ERROR_I18N("Create compare result failed");
798         return nullptr;
799     }
800 
801     return result;
802 }
803 
GetCollatorResolvedOptions(napi_env env,napi_callback_info info)804 napi_value IntlAddon::GetCollatorResolvedOptions(napi_env env, napi_callback_info info)
805 {
806     napi_value thisVar = nullptr;
807     void *data = nullptr;
808     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
809 
810     IntlAddon *obj = nullptr;
811     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
812     if (status != napi_ok || !obj || !obj->collator_) {
813         HILOG_ERROR_I18N("GetCollatorResolvedOptions: Get Collator object failed");
814         return nullptr;
815     }
816     napi_value result = nullptr;
817     napi_create_object(env, &result);
818     std::map<std::string, std::string> options = {};
819     obj->collator_->ResolvedOptions(options);
820     JSUtils::SetOptionProperties(env, result, options, "localeMatcher");
821     JSUtils::SetOptionProperties(env, result, options, "locale");
822     JSUtils::SetOptionProperties(env, result, options, "usage");
823     JSUtils::SetOptionProperties(env, result, options, "sensitivity");
824     JSUtils::SetBooleanOptionProperties(env, result, options, "ignorePunctuation");
825     JSUtils::SetBooleanOptionProperties(env, result, options, "numeric");
826     JSUtils::SetOptionProperties(env, result, options, "caseFirst");
827     JSUtils::SetOptionProperties(env, result, options, "collation");
828     return result;
829 }
830 
GetPluralRulesType(napi_env env,napi_value options,std::map<std::string,std::string> & map)831 void GetPluralRulesType(napi_env env, napi_value options, std::map<std::string, std::string> &map)
832 {
833     JSUtils::GetOptionValue(env, options, "type", map);
834     auto it = map.find("type");
835     if (it != map.end()) {
836         std::string type = it->second;
837         if (type != "cardinal" && type != "ordinal") {
838             HILOG_ERROR_I18N("invalid type");
839             return;
840         }
841     } else {
842         map.insert(std::make_pair("type", "cardinal"));
843     }
844 }
845 
GetPluralRulesInteger(napi_env env,napi_value options,std::map<std::string,std::string> & map)846 void GetPluralRulesInteger(napi_env env, napi_value options, std::map<std::string, std::string> &map)
847 {
848     JSUtils::GetIntegerOptionValue(env, options, "minimumIntegerDigits", map);
849     auto it = map.find("minimumIntegerDigits");
850     if (it != map.end()) {
851         std::string minimumIntegerDigits = it->second;
852         int32_t status = 0;
853         int n = ConvertString2Int(minimumIntegerDigits, status);
854         if (status == -1 || n < 1 || n > 21) {  // the valid range of minimumIntegerDigits is [1, 21]
855             HILOG_ERROR_I18N("invalid minimumIntegerDigits");
856             return;
857         }
858     } else {
859         map.insert(std::make_pair("minimumIntegerDigits", std::to_string(1)));
860     }
861 }
862 
GetPluralRulesFractions(napi_env env,napi_value options,std::map<std::string,std::string> & map)863 void GetPluralRulesFractions(napi_env env, napi_value options, std::map<std::string, std::string> &map)
864 {
865     JSUtils::GetIntegerOptionValue(env, options, "minimumFractionDigits", map);
866     auto it = map.find("minimumFractionDigits");
867     if (it != map.end()) {
868         std::string minimumFractionDigits = it->second;
869         int32_t status = 0;
870         int n = ConvertString2Int(minimumFractionDigits, status);
871         if (status == -1 || n < 0 || n > 20) {  // the valid range of minimumFractionDigits is [0, 20]
872             HILOG_ERROR_I18N("invalid minimumFractionDigits");
873             return;
874         }
875     }
876 
877     JSUtils::GetIntegerOptionValue(env, options, "maximumFractionDigits", map);
878     it = map.find("maximumFractionDigits");
879     if (it != map.end()) {
880         std::string maximumFractionDigits = it->second;
881         int32_t status = 0;
882         int n = ConvertString2Int(maximumFractionDigits, status);
883         if (status == -1 || n < 0 || n > 20) {  // the valid range of maximumFractionDigits is [0, 20]
884             HILOG_ERROR_I18N("invalid maximumFractionDigits");
885             return;
886         }
887     }
888 }
889 
GetPluralRulesSignificant(napi_env env,napi_value options,std::map<std::string,std::string> & map)890 void GetPluralRulesSignificant(napi_env env, napi_value options, std::map<std::string, std::string> &map)
891 {
892     int minSignificant = -1;
893     JSUtils::GetIntegerOptionValue(env, options, "minimumSignificantDigits", map);
894     auto it = map.find("minimumSignificantDigits");
895     if (it != map.end()) {
896         std::string minSignificantStr = it->second;
897         int32_t status = 0;
898         int minSignificantInt = ConvertString2Int(minSignificantStr, status);
899         // the valid range of minSignificantInt is [1, 21]
900         if (status == -1 || minSignificantInt < 1 || minSignificantInt > 21) {
901             HILOG_ERROR_I18N("invalid minimumSignificantDigits");
902             return;
903         }
904         minSignificant = minSignificantInt;
905     } else {
906         minSignificant = 1;
907     }
908 
909     JSUtils::GetIntegerOptionValue(env, options, "maximumSignificantDigits", map);
910     it = map.find("maximumSignificantDigits");
911     if (it != map.end()) {
912         std::string maxSignificantStr = it->second;
913         int32_t status = 0;
914         int maxSignificant = ConvertString2Int(maxSignificantStr, status);
915         // the valid range of minSignificant is [minSignificant, 21]
916         if (status == -1 || maxSignificant < minSignificant || maxSignificant > 21) {
917             HILOG_ERROR_I18N("invalid maximumSignificantDigits");
918             return;
919         }
920     }
921 }
922 
GetPluralRulesOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)923 void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
924 {
925     GetCollatorLocaleMatcher(env, options, map);
926     GetPluralRulesType(env, options, map);
927     GetPluralRulesInteger(env, options, map);
928     GetPluralRulesFractions(env, options, map);
929     GetPluralRulesSignificant(env, options, map);
930 }
931 
InitPluralRules(napi_env env,napi_value exports)932 napi_value IntlAddon::InitPluralRules(napi_env env, napi_value exports)
933 {
934     napi_status status = napi_ok;
935     napi_property_descriptor properties[] = {
936         DECLARE_NAPI_FUNCTION("select", Select)
937     };
938 
939     napi_value constructor = nullptr;
940     status = napi_define_class(env, "PluralRules", NAPI_AUTO_LENGTH, PluralRulesConstructor, nullptr,
941         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
942     if (status != napi_ok) {
943         HILOG_ERROR_I18N("Define class failed when InitPluralRules");
944         return nullptr;
945     }
946 
947     status = napi_set_named_property(env, exports, "PluralRules", constructor);
948     if (status != napi_ok) {
949         HILOG_ERROR_I18N("Set property failed when InitPluralRules");
950         return nullptr;
951     }
952     return exports;
953 }
954 
PluralRulesConstructor(napi_env env,napi_callback_info info)955 napi_value IntlAddon::PluralRulesConstructor(napi_env env, napi_callback_info info)
956 {
957     size_t argc = 2;
958     napi_value argv[2] = { nullptr };
959     napi_value thisVar = nullptr;
960     void *data = nullptr;
961     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
962     if (status != napi_ok) {
963         return nullptr;
964     }
965     napi_valuetype valueType = napi_valuetype::napi_undefined;
966     std::vector<std::string> localeTags;
967     if (argc > 0) {
968         napi_typeof(env, argv[0], &valueType);
969         bool isArray = false;
970         napi_is_array(env, argv[0], &isArray);
971         if (valueType == napi_valuetype::napi_string) {
972             JSUtils::GetLocaleTags(env, argv[0], localeTags);
973         } else if (isArray) {
974             uint32_t arrayLength = 0;
975             napi_get_array_length(env, argv[0], &arrayLength);
976             napi_value element = nullptr;
977             for (uint32_t i = 0; i < arrayLength; i++) {
978                 napi_get_element(env, argv[0], i, &element);
979                 JSUtils::GetLocaleTags(env, element, localeTags);
980             }
981         }
982     }
983     std::map<std::string, std::string> map = {};
984     if (argc > 1) {
985         GetPluralRulesOptionValues(env, argv[1], map);
986     }
987     std::unique_ptr<IntlAddon> obj = nullptr;
988     obj = std::make_unique<IntlAddon>();
989     status =
990         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
991     if (status != napi_ok) {
992         HILOG_ERROR_I18N("PluralRulesConstructor: Wrap IntlAddon failed");
993         return nullptr;
994     }
995     if (!obj->InitPluralRulesContext(env, info, localeTags, map)) {
996         HILOG_ERROR_I18N("PluralRulesConstructor: Init DateTimeFormat failed");
997         return nullptr;
998     }
999     obj.release();
1000     return thisVar;
1001 }
1002 
InitPluralRulesContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)1003 bool IntlAddon::InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
1004     std::map<std::string, std::string> &map)
1005 {
1006     napi_value global = nullptr;
1007     napi_status status = napi_get_global(env, &global);
1008     if (status != napi_ok) {
1009         HILOG_ERROR_I18N("InitPluralRulesContext: Get global failed");
1010         return false;
1011     }
1012     env_ = env;
1013     pluralrules_ = std::make_unique<PluralRules>(localeTags, map);
1014 
1015     return pluralrules_ != nullptr;
1016 }
1017 
Select(napi_env env,napi_callback_info info)1018 napi_value IntlAddon::Select(napi_env env, napi_callback_info info)
1019 {
1020     size_t argc = 1;
1021     napi_value argv[1] = { 0 };
1022     napi_value thisVar = nullptr;
1023     void *data = nullptr;
1024     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1025     napi_valuetype valueType = napi_valuetype::napi_undefined;
1026     napi_typeof(env, argv[0], &valueType);
1027     if (valueType != napi_valuetype::napi_number) {
1028         HILOG_ERROR_I18N("Select: Parameter type does not match");
1029         return nullptr;
1030     }
1031 
1032     double number = 0;
1033     napi_status status = napi_get_value_double(env, argv[0], &number);
1034     if (status != napi_ok) {
1035         HILOG_ERROR_I18N("Select: Get number failed");
1036         return nullptr;
1037     }
1038 
1039     IntlAddon *obj = nullptr;
1040     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1041     if (status != napi_ok || !obj || !obj->pluralrules_) {
1042         HILOG_ERROR_I18N("Get PluralRules object failed");
1043         return nullptr;
1044     }
1045 
1046     std::string res = obj->pluralrules_->Select(number);
1047     napi_value result = nullptr;
1048     status = napi_create_string_utf8(env, res.c_str(), NAPI_AUTO_LENGTH, &result);
1049     if (status != napi_ok) {
1050         HILOG_ERROR_I18N("get select result failed");
1051         return nullptr;
1052     }
1053     return result;
1054 }
1055 
Init(napi_env env,napi_value exports)1056 napi_value Init(napi_env env, napi_value exports)
1057 {
1058     napi_value val = LocaleInfoAddon::InitLocale(env, exports);
1059     val = IntlAddon::InitDateTimeFormat(env, val);
1060     val = IntlAddon::InitCollator(env, val);
1061     val = IntlAddon::InitRelativeTimeFormat(env, val);
1062     val = IntlAddon::InitPluralRules(env, val);
1063     val = NumberFormatAddon::InitNumberFormat(env, val);
1064     return val;
1065 }
1066 
1067 static napi_module g_intlModule = {
1068     .nm_version = 1,
1069     .nm_flags = 0,
1070     .nm_filename = nullptr,
1071     .nm_register_func = Init,
1072     .nm_modname = "intl",
1073     .nm_priv = nullptr,
1074     .reserved = { 0 }
1075 };
1076 
AbilityRegister()1077 extern "C" __attribute__((constructor)) void AbilityRegister()
1078 {
1079     napi_module_register(&g_intlModule);
1080 }
1081 } // namespace I18n
1082 } // namespace Global
1083 } // namespace OHOS
1084