• 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 
21 #include "hilog/log.h"
22 #include "node_api.h"
23 
24 namespace OHOS {
25 namespace Global {
26 namespace I18n {
27 static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "IntlJs" };
28 using namespace OHOS::HiviewDFX;
29 static thread_local napi_ref *g_constructor = nullptr;
30 
IntlAddon()31 IntlAddon::IntlAddon() : env_(nullptr) {}
32 
~IntlAddon()33 IntlAddon::~IntlAddon()
34 {
35 }
36 
Destructor(napi_env env,void * nativeObject,void * hint)37 void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint)
38 {
39     if (!nativeObject) {
40         return;
41     }
42     delete reinterpret_cast<IntlAddon *>(nativeObject);
43     nativeObject = nullptr;
44 }
45 
InitLocale(napi_env env,napi_value exports)46 napi_value IntlAddon::InitLocale(napi_env env, napi_value exports)
47 {
48     napi_status status = napi_ok;
49     napi_property_descriptor properties[] = {
50         DECLARE_NAPI_GETTER("language", GetLanguage),
51         DECLARE_NAPI_GETTER("baseName", GetBaseName),
52         DECLARE_NAPI_GETTER("region", GetRegion),
53         DECLARE_NAPI_GETTER("script", GetScript),
54         DECLARE_NAPI_GETTER("calendar", GetCalendar),
55         DECLARE_NAPI_GETTER("collation", GetCollation),
56         DECLARE_NAPI_GETTER("hourCycle", GetHourCycle),
57         DECLARE_NAPI_GETTER("numberingSystem", GetNumberingSystem),
58         DECLARE_NAPI_GETTER("numeric", GetNumeric),
59         DECLARE_NAPI_GETTER("caseFirst", GetCaseFirst),
60         DECLARE_NAPI_FUNCTION("toString", ToString),
61         DECLARE_NAPI_FUNCTION("minimize", Minimize),
62         DECLARE_NAPI_FUNCTION("maximize", Maximize),
63     };
64 
65     napi_value constructor = nullptr;
66     status = napi_define_class(env, "Locale", NAPI_AUTO_LENGTH, LocaleConstructor, nullptr,
67         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
68     if (status != napi_ok) {
69         HiLog::Error(LABEL, "Define class failed when InitLocale");
70         return nullptr;
71     }
72 
73     status = napi_set_named_property(env, exports, "Locale", constructor);
74     if (status != napi_ok) {
75         HiLog::Error(LABEL, "Set property failed when InitLocale");
76         return nullptr;
77     }
78     g_constructor = new (std::nothrow) napi_ref;
79     if (!g_constructor) {
80         HiLog::Error(LABEL, "Failed to create ref at init");
81         return nullptr;
82     }
83     status = napi_create_reference(env, constructor, 1, g_constructor);
84     if (status != napi_ok) {
85         HiLog::Error(LABEL, "Failed to create reference at init");
86         return nullptr;
87     }
88     return exports;
89 }
90 
InitDateTimeFormat(napi_env env,napi_value exports)91 napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports)
92 {
93     napi_status status = napi_ok;
94     napi_property_descriptor properties[] = {
95         DECLARE_NAPI_FUNCTION("format", FormatDateTime),
96         DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange),
97         DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions)
98     };
99 
100     napi_value constructor = nullptr;
101     status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr,
102         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
103     if (status != napi_ok) {
104         HiLog::Error(LABEL, "Define class failed when InitDateTimeFormat");
105         return nullptr;
106     }
107 
108     status = napi_set_named_property(env, exports, "DateTimeFormat", constructor);
109     if (status != napi_ok) {
110         HiLog::Error(LABEL, "Set property failed when InitDateTimeFormat");
111         return nullptr;
112     }
113     return exports;
114 }
115 
InitRelativeTimeFormat(napi_env env,napi_value exports)116 napi_value IntlAddon::InitRelativeTimeFormat(napi_env env, napi_value exports)
117 {
118     napi_status status = napi_ok;
119     napi_property_descriptor properties[] = {
120         DECLARE_NAPI_FUNCTION("format", FormatRelativeTime),
121         DECLARE_NAPI_FUNCTION("formatToParts", FormatToParts),
122         DECLARE_NAPI_FUNCTION("resolvedOptions", GetRelativeTimeResolvedOptions)
123     };
124 
125     napi_value constructor = nullptr;
126     status = napi_define_class(env, "RelativeTimeFormat", NAPI_AUTO_LENGTH, RelativeTimeFormatConstructor, nullptr,
127         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
128     if (status != napi_ok) {
129         HiLog::Error(LABEL, "Define class failed when InitRelativeTimeFormat");
130         return nullptr;
131     }
132 
133     status = napi_set_named_property(env, exports, "RelativeTimeFormat", constructor);
134     if (status != napi_ok) {
135         HiLog::Error(LABEL, "Set property failed when InitRelativeTimeFormat");
136         return nullptr;
137     }
138     return exports;
139 }
140 
InitNumberFormat(napi_env env,napi_value exports)141 napi_value IntlAddon::InitNumberFormat(napi_env env, napi_value exports)
142 {
143     napi_status status = napi_ok;
144     napi_property_descriptor properties[] = {
145         DECLARE_NAPI_FUNCTION("format", FormatNumber),
146         DECLARE_NAPI_FUNCTION("resolvedOptions", GetNumberResolvedOptions)
147     };
148 
149     napi_value constructor = nullptr;
150     status = napi_define_class(env, "NumberFormat", NAPI_AUTO_LENGTH, NumberFormatConstructor, nullptr,
151         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
152     if (status != napi_ok) {
153         HiLog::Error(LABEL, "Define class failed when InitNumberFormat");
154         return nullptr;
155     }
156 
157     status = napi_set_named_property(env, exports, "NumberFormat", constructor);
158     if (status != napi_ok) {
159         HiLog::Error(LABEL, "Set property failed when InitNumberFormat");
160         return nullptr;
161     }
162     return exports;
163 }
164 
GetOptionValue(napi_env env,napi_value options,const std::string & optionName,std::map<std::string,std::string> & map)165 void GetOptionValue(napi_env env, napi_value options, const std::string &optionName,
166     std::map<std::string, std::string> &map)
167 {
168     napi_value optionValue = nullptr;
169     napi_valuetype type = napi_undefined;
170     napi_status status = napi_typeof(env, options, &type);
171     if (status != napi_ok && type != napi_object) {
172         HiLog::Error(LABEL, "Get option failed, option is not an object");
173         return;
174     }
175     bool hasProperty = false;
176     napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
177     if (propStatus == napi_ok && hasProperty) {
178         status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
179         if (status == napi_ok) {
180             size_t len = 0;
181             napi_get_value_string_utf8(env, optionValue, nullptr, 0, &len);
182             std::vector<char> optionBuf(len + 1);
183             status = napi_get_value_string_utf8(env, optionValue, optionBuf.data(), len + 1, &len);
184             if (status != napi_ok) {
185                 return;
186             }
187             map.insert(make_pair(optionName, optionBuf.data()));
188         }
189     }
190 }
191 
GetIntegerOptionValue(napi_env env,napi_value options,const std::string & optionName,std::map<std::string,std::string> & map)192 void GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName,
193     std::map<std::string, std::string> &map)
194 {
195     napi_value optionValue = nullptr;
196     napi_valuetype type = napi_undefined;
197     napi_status status = napi_typeof(env, options, &type);
198     if (status != napi_ok && type != napi_object) {
199         HiLog::Error(LABEL, "Set option failed, option is not an object");
200         return;
201     }
202     bool hasProperty = false;
203     napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
204     if (propStatus == napi_ok && hasProperty) {
205         status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
206         if (status == napi_ok) {
207             int64_t integerValue = -1;
208             status = napi_get_value_int64(env, optionValue, &integerValue);
209             if (status == napi_ok) {
210                 map.insert(make_pair(optionName, std::to_string(integerValue)));
211             }
212         }
213     }
214 }
215 
GetBoolOptionValue(napi_env env,napi_value options,const std::string & optionName,std::map<std::string,std::string> & map)216 void GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName,
217     std::map<std::string, std::string> &map)
218 {
219     napi_value optionValue = nullptr;
220     napi_valuetype type = napi_undefined;
221     napi_status status = napi_typeof(env, options, &type);
222     if (status != napi_ok && type != napi_object) {
223         HiLog::Error(LABEL, "Set option failed, option is not an object");
224         return;
225     }
226     bool hasProperty = false;
227     napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
228     if (propStatus == napi_ok && hasProperty) {
229         status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
230         if (status == napi_ok) {
231             bool boolValue = false;
232             napi_get_value_bool(env, optionValue, &boolValue);
233             std::string value = boolValue ? "true" : "false";
234             map.insert(make_pair(optionName, value));
235         }
236     }
237 }
238 
GetDateOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)239 void GetDateOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
240 {
241     GetOptionValue(env, options, "calendar", map);
242     GetOptionValue(env, options, "dateStyle", map);
243     GetOptionValue(env, options, "timeStyle", map);
244     GetOptionValue(env, options, "hourCycle", map);
245     GetOptionValue(env, options, "timeZone", map);
246     GetOptionValue(env, options, "timeZoneName", map);
247     GetOptionValue(env, options, "numberingSystem", map);
248     GetBoolOptionValue(env, options, "hour12", map);
249     GetOptionValue(env, options, "weekday", map);
250     GetOptionValue(env, options, "era", map);
251     GetOptionValue(env, options, "year", map);
252     GetOptionValue(env, options, "month", map);
253     GetOptionValue(env, options, "day", map);
254     GetOptionValue(env, options, "hour", map);
255     GetOptionValue(env, options, "minute", map);
256     GetOptionValue(env, options, "second", map);
257     GetOptionValue(env, options, "localeMatcher", map);
258     GetOptionValue(env, options, "formatMatcher", map);
259     GetOptionValue(env, options, "dayPeriod", map);
260 }
261 
GetRelativeTimeOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)262 void GetRelativeTimeOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
263 {
264     GetOptionValue(env, options, "localeMatcher", map);
265     GetOptionValue(env, options, "numeric", map);
266     GetOptionValue(env, options, "style", map);
267 }
268 
GetLocaleTag(napi_env env,napi_value argv)269 std::string GetLocaleTag(napi_env env, napi_value argv)
270 {
271     std::string localeTag = "";
272     std::vector<char> buf;
273     if (argv != nullptr) {
274         napi_valuetype valueType = napi_valuetype::napi_undefined;
275         napi_typeof(env, argv, &valueType);
276         if (valueType != napi_valuetype::napi_string) {
277             napi_throw_type_error(env, nullptr, "Parameter type does not match");
278             return "";
279         }
280         size_t len = 0;
281         napi_status status = napi_get_value_string_utf8(env, argv, nullptr, 0, &len);
282         if (status != napi_ok) {
283             HiLog::Error(LABEL, "Get locale tag length failed");
284             return "";
285         }
286         buf.resize(len + 1);
287         status = napi_get_value_string_utf8(env, argv, buf.data(), len + 1, &len);
288         if (status != napi_ok) {
289             HiLog::Error(LABEL, "Get locale tag failed");
290             return "";
291         }
292         localeTag = buf.data();
293     } else {
294         localeTag = "";
295     }
296     return localeTag;
297 }
298 
LocaleConstructor(napi_env env,napi_callback_info info)299 napi_value IntlAddon::LocaleConstructor(napi_env env, napi_callback_info info)
300 {
301     size_t argc = 2;
302     napi_value argv[2] = { 0 };
303     napi_value thisVar = nullptr;
304     void *data = nullptr;
305     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
306     if (status != napi_ok) {
307         return nullptr;
308     }
309     std::string localeTag = GetLocaleTag(env, argv[0]);
310 
311     std::map<std::string, std::string> map = {};
312     if (argv[1] != nullptr) {
313         GetOptionValue(env, argv[1], "calendar", map);
314         GetOptionValue(env, argv[1], "collation", map);
315         GetOptionValue(env, argv[1], "hourCycle", map);
316         GetOptionValue(env, argv[1], "numberingSystem", map);
317         GetBoolOptionValue(env, argv[1], "numeric", map);
318         GetOptionValue(env, argv[1], "caseFirst", map);
319     }
320     std::unique_ptr<IntlAddon> obj = nullptr;
321     obj = std::make_unique<IntlAddon>();
322     status =
323         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
324     if (status != napi_ok) {
325         HiLog::Error(LABEL, "Wrap IntlAddon failed");
326         return nullptr;
327     }
328     if (!obj->InitLocaleContext(env, info, localeTag, map)) {
329         return nullptr;
330     }
331     obj.release();
332     return thisVar;
333 }
334 
InitLocaleContext(napi_env env,napi_callback_info info,const std::string localeTag,std::map<std::string,std::string> & map)335 bool IntlAddon::InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag,
336     std::map<std::string, std::string> &map)
337 {
338     napi_value global = nullptr;
339     napi_status status = napi_get_global(env, &global);
340     if (status != napi_ok) {
341         HiLog::Error(LABEL, "Get global failed");
342         return false;
343     }
344     env_ = env;
345     locale_ = std::make_unique<LocaleInfo>(localeTag, map);
346 
347     return locale_ != nullptr;
348 }
349 
GetLocaleTags(napi_env env,napi_value rawLocaleTag,std::vector<std::string> & localeTags)350 void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector<std::string> &localeTags)
351 {
352     size_t len = 0;
353     napi_status status = napi_get_value_string_utf8(env, rawLocaleTag, nullptr, 0, &len);
354     if (status != napi_ok) {
355         HiLog::Error(LABEL, "Get locale tag length failed");
356         return;
357     }
358     std::vector<char> buf(len + 1);
359     status = napi_get_value_string_utf8(env, rawLocaleTag, buf.data(), len + 1, &len);
360     if (status != napi_ok) {
361         HiLog::Error(LABEL, "Get locale tag failed");
362         return;
363     }
364     localeTags.push_back(buf.data());
365 }
366 
DateTimeFormatConstructor(napi_env env,napi_callback_info info)367 napi_value IntlAddon::DateTimeFormatConstructor(napi_env env, napi_callback_info info)
368 {
369     size_t argc = 2;
370     napi_value argv[2] = { 0 };
371     napi_value thisVar = nullptr;
372     void *data = nullptr;
373     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
374     if (status != napi_ok) {
375         return nullptr;
376     }
377     std::vector<std::string> localeTags;
378     if (argv[0] != nullptr) {
379         napi_valuetype valueType = napi_valuetype::napi_undefined;
380         napi_typeof(env, argv[0], &valueType);
381         bool isArray = false;
382         napi_is_array(env, argv[0], &isArray);
383         if (valueType == napi_valuetype::napi_string) {
384             GetLocaleTags(env, argv[0], localeTags);
385         } else if (isArray) {
386             uint32_t arrayLength = 0;
387             napi_get_array_length(env, argv[0], &arrayLength);
388             napi_value element = nullptr;
389             for (uint32_t i = 0; i < arrayLength; i++) {
390                 napi_get_element(env, argv[0], i, &element);
391                 GetLocaleTags(env, element, localeTags);
392             }
393         }
394     }
395     std::map<std::string, std::string> map = {};
396     if (argv[1] != nullptr) {
397         GetDateOptionValues(env, argv[1], map);
398     }
399     std::unique_ptr<IntlAddon> obj = nullptr;
400     obj = std::make_unique<IntlAddon>();
401     status =
402         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
403     if (status != napi_ok) {
404         HiLog::Error(LABEL, "Wrap IntlAddon failed");
405         return nullptr;
406     }
407     if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) {
408         HiLog::Error(LABEL, "Init DateTimeFormat failed");
409         return nullptr;
410     }
411     obj.release();
412     return thisVar;
413 }
414 
InitDateTimeFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)415 bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
416     std::map<std::string, std::string> &map)
417 {
418     napi_value global = nullptr;
419     napi_status status = napi_get_global(env, &global);
420     if (status != napi_ok) {
421         HiLog::Error(LABEL, "Get global failed");
422         return false;
423     }
424     env_ = env;
425     datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
426 
427     return datefmt_ != nullptr;
428 }
429 
RelativeTimeFormatConstructor(napi_env env,napi_callback_info info)430 napi_value IntlAddon::RelativeTimeFormatConstructor(napi_env env, napi_callback_info info)
431 {
432     size_t argc = 2;
433     napi_value argv[2] = { 0 };
434     napi_value thisVar = nullptr;
435     void *data = nullptr;
436     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
437     if (status != napi_ok) {
438         return nullptr;
439     }
440     std::vector<std::string> localeTags;
441     if (argv[0] != nullptr) {
442         napi_valuetype valueType = napi_valuetype::napi_undefined;
443         napi_typeof(env, argv[0], &valueType);
444         bool isArray = false;
445         napi_is_array(env, argv[0], &isArray);
446         if (valueType == napi_valuetype::napi_string) {
447             GetLocaleTags(env, argv[0], localeTags);
448         } else if (isArray) {
449             uint32_t arrayLength = 0;
450             napi_get_array_length(env, argv[0], &arrayLength);
451             napi_value element = nullptr;
452             for (uint32_t i = 0; i < arrayLength; i++) {
453                 napi_get_element(env, argv[0], i, &element);
454                 GetLocaleTags(env, element, localeTags);
455             }
456         }
457     }
458     std::map<std::string, std::string> map = {};
459     if (argv[1] != nullptr) {
460         GetRelativeTimeOptionValues(env, argv[1], map);
461     }
462     std::unique_ptr<IntlAddon> obj = nullptr;
463     obj = std::make_unique<IntlAddon>();
464     status =
465         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
466     if (status != napi_ok) {
467         HiLog::Error(LABEL, "Wrap IntlAddon failed");
468         return nullptr;
469     }
470     if (!obj->InitRelativeTimeFormatContext(env, info, localeTags, map)) {
471         HiLog::Error(LABEL, "Init RelativeTimeFormat failed");
472         return nullptr;
473     }
474     obj.release();
475     return thisVar;
476 }
477 
InitRelativeTimeFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)478 bool IntlAddon::InitRelativeTimeFormatContext(napi_env env, napi_callback_info info,
479     std::vector<std::string> localeTags, std::map<std::string, std::string> &map)
480 {
481     env_ = env;
482     relativetimefmt_ = std::make_unique<RelativeTimeFormat>(localeTags, map);
483 
484     return relativetimefmt_ != nullptr;
485 }
486 
FormatDateTime(napi_env env,napi_callback_info info)487 napi_value IntlAddon::FormatDateTime(napi_env env, napi_callback_info info)
488 {
489     size_t argc = 1;
490     napi_value argv[1] = { 0 };
491     napi_value thisVar = nullptr;
492     void *data = nullptr;
493     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
494 
495     int64_t milliseconds = GetMilliseconds(env, argv, 0);
496     if (milliseconds == -1) {
497         return nullptr;
498     }
499     IntlAddon *obj = nullptr;
500     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
501     if (status != napi_ok || !obj || !obj->datefmt_) {
502         HiLog::Error(LABEL, "Get DateTimeFormat object failed");
503         return nullptr;
504     }
505     std::string value = obj->datefmt_->Format(milliseconds);
506     napi_value result = nullptr;
507     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
508     if (status != napi_ok) {
509         HiLog::Error(LABEL, "Create format string failed");
510         return nullptr;
511     }
512     return result;
513 }
514 
FormatDateTimeRange(napi_env env,napi_callback_info info)515 napi_value IntlAddon::FormatDateTimeRange(napi_env env, napi_callback_info info)
516 {
517     size_t argc = 2;
518     napi_value argv[2] = { 0 };
519     napi_value thisVar = nullptr;
520     void *data = nullptr;
521     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
522     if (argv[0] == nullptr || argv[1] == nullptr) {
523         HiLog::Error(LABEL, "Parameter wrong");
524         return nullptr;
525     }
526     int64_t firstMilliseconds = GetMilliseconds(env, argv, 0);
527     int64_t secondMilliseconds = GetMilliseconds(env, argv, 1);
528     if (firstMilliseconds == -1 || secondMilliseconds == -1) {
529         return nullptr;
530     }
531     IntlAddon *obj = nullptr;
532     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
533     if (status != napi_ok || !obj || !obj->datefmt_) {
534         HiLog::Error(LABEL, "Get DateTimeFormat object failed");
535         return nullptr;
536     }
537     std::string value = obj->datefmt_->FormatRange(firstMilliseconds, secondMilliseconds);
538     napi_value result = nullptr;
539     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
540     if (status != napi_ok) {
541         HiLog::Error(LABEL, "Create format string failed");
542         return nullptr;
543     }
544     return result;
545 }
546 
GetNumberOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)547 void GetNumberOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
548 {
549     GetOptionValue(env, options, "currency", map);
550     GetOptionValue(env, options, "currencySign", map);
551     GetOptionValue(env, options, "currencyDisplay", map);
552     GetOptionValue(env, options, "unit", map);
553     GetOptionValue(env, options, "unitDisplay", map);
554     GetOptionValue(env, options, "compactDisplay", map);
555     GetOptionValue(env, options, "signDisplay", map);
556     GetOptionValue(env, options, "localeMatcher", map);
557     GetOptionValue(env, options, "style", map);
558     GetOptionValue(env, options, "numberingSystem", map);
559     GetOptionValue(env, options, "notation", map);
560     GetOptionValue(env, options, "unitUsage", map);
561     GetBoolOptionValue(env, options, "useGrouping", map);
562     GetIntegerOptionValue(env, options, "minimumIntegerDigits", map);
563     GetIntegerOptionValue(env, options, "minimumFractionDigits", map);
564     GetIntegerOptionValue(env, options, "maximumFractionDigits", map);
565     GetIntegerOptionValue(env, options, "minimumSignificantDigits", map);
566     GetIntegerOptionValue(env, options, "maximumSignificantDigits", map);
567 }
568 
NumberFormatConstructor(napi_env env,napi_callback_info info)569 napi_value IntlAddon::NumberFormatConstructor(napi_env env, napi_callback_info info)
570 {
571     size_t argc = 2;
572     napi_value argv[2] = { 0 };
573     napi_value thisVar = nullptr;
574     void *data = nullptr;
575     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
576     if (status != napi_ok) {
577         return nullptr;
578     }
579     std::vector<std::string> localeTags;
580     if (argv[0] != nullptr) {
581         napi_valuetype valueType = napi_valuetype::napi_undefined;
582         napi_typeof(env, argv[0], &valueType);
583         bool isArray = false;
584         napi_is_array(env, argv[0], &isArray);
585 
586         if (valueType == napi_valuetype::napi_string) {
587             GetLocaleTags(env, argv[0], localeTags);
588         } else if (isArray) {
589             uint32_t arrayLength = 0;
590             napi_get_array_length(env, argv[0], &arrayLength);
591             napi_value element = nullptr;
592             for (uint32_t i = 0; i < arrayLength; i++) {
593                 napi_get_element(env, argv[0], i, &element);
594                 GetLocaleTags(env, element, localeTags);
595             }
596         }
597     }
598     std::map<std::string, std::string> map = {};
599     if (argv[1] != nullptr) {
600         GetNumberOptionValues(env, argv[1], map);
601     }
602     std::unique_ptr<IntlAddon> obj = nullptr;
603     obj = std::make_unique<IntlAddon>();
604     status =
605         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
606     if (status != napi_ok) {
607         HiLog::Error(LABEL, "Wrap IntlAddon failed");
608         return nullptr;
609     }
610     if (!obj->InitNumberFormatContext(env, info, localeTags, map)) {
611         HiLog::Error(LABEL, "Init NumberFormat failed");
612         return nullptr;
613     }
614     obj.release();
615     return thisVar;
616 }
617 
InitNumberFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)618 bool IntlAddon::InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
619     std::map<std::string, std::string> &map)
620 {
621     napi_value global = nullptr;
622     napi_status status = napi_get_global(env, &global);
623     if (status != napi_ok) {
624         HiLog::Error(LABEL, "Get global failed");
625         return false;
626     }
627     env_ = env;
628     numberfmt_ = std::make_unique<NumberFormat>(localeTags, map);
629 
630     return numberfmt_ != nullptr;
631 }
632 
GetMilliseconds(napi_env env,napi_value * argv,int index)633 int64_t IntlAddon::GetMilliseconds(napi_env env, napi_value *argv, int index)
634 {
635     napi_value funcGetDateInfo = nullptr;
636     napi_status status = napi_get_named_property(env, argv[index], "getTime", &funcGetDateInfo);
637     if (status != napi_ok) {
638         HiLog::Error(LABEL, "Get Milliseconds property failed");
639         return -1;
640     }
641     napi_value ret_value = nullptr;
642     status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value);
643     if (status != napi_ok) {
644         HiLog::Error(LABEL, "Get Milliseconds function failed");
645         return -1;
646     }
647     int64_t milliseconds = 0;
648     status = napi_get_value_int64(env, ret_value, &milliseconds);
649     if (status != napi_ok) {
650         HiLog::Error(LABEL, "Get Milliseconds failed");
651         return -1;
652     }
653     return milliseconds;
654 }
655 
GetLanguage(napi_env env,napi_callback_info info)656 napi_value IntlAddon::GetLanguage(napi_env env, napi_callback_info info)
657 {
658     size_t argc = 0;
659     napi_value argv[0];
660     napi_value thisVar = nullptr;
661     void *data = nullptr;
662     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
663 
664     IntlAddon *obj = nullptr;
665     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
666     if (status != napi_ok || !obj || !obj->locale_) {
667         HiLog::Error(LABEL, "Get Locale object failed");
668         return nullptr;
669     }
670     std::string value = obj->locale_->GetLanguage();
671 
672     napi_value result = nullptr;
673     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
674     if (status != napi_ok) {
675         HiLog::Error(LABEL, "Create language string failed");
676         return nullptr;
677     }
678     return result;
679 }
680 
GetScript(napi_env env,napi_callback_info info)681 napi_value IntlAddon::GetScript(napi_env env, napi_callback_info info)
682 {
683     size_t argc = 0;
684     napi_value argv[0];
685     napi_value thisVar = nullptr;
686     void *data = nullptr;
687     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
688 
689     IntlAddon *obj = nullptr;
690     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
691     if (status != napi_ok || !obj || !obj->locale_) {
692         HiLog::Error(LABEL, "Get Locale object failed");
693         return nullptr;
694     }
695     std::string value = obj->locale_->GetScript();
696 
697     napi_value result = nullptr;
698     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
699     if (status != napi_ok) {
700         HiLog::Error(LABEL, "Create script string failed");
701         return nullptr;
702     }
703     return result;
704 }
705 
GetRegion(napi_env env,napi_callback_info info)706 napi_value IntlAddon::GetRegion(napi_env env, napi_callback_info info)
707 {
708     size_t argc = 0;
709     napi_value argv[0];
710     napi_value thisVar = nullptr;
711     void *data = nullptr;
712     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
713 
714     IntlAddon *obj = nullptr;
715     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
716     if (status != napi_ok || !obj || !obj->locale_) {
717         HiLog::Error(LABEL, "Get Locale object failed");
718         return nullptr;
719     }
720     std::string value = obj->locale_->GetRegion();
721 
722     napi_value result = nullptr;
723     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
724     if (status != napi_ok) {
725         HiLog::Error(LABEL, "Create region string failed");
726         return nullptr;
727     }
728     return result;
729 }
730 
GetBaseName(napi_env env,napi_callback_info info)731 napi_value IntlAddon::GetBaseName(napi_env env, napi_callback_info info)
732 {
733     size_t argc = 0;
734     napi_value argv[0];
735     napi_value thisVar = nullptr;
736     void *data = nullptr;
737     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
738 
739     IntlAddon *obj = nullptr;
740     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
741     if (status != napi_ok || !obj || !obj->locale_) {
742         HiLog::Error(LABEL, "Get Locale object failed");
743         return nullptr;
744     }
745     std::string value = obj->locale_->GetBaseName();
746 
747     napi_value result = nullptr;
748     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
749     if (status != napi_ok) {
750         HiLog::Error(LABEL, "Create base name string failed");
751         return nullptr;
752     }
753     return result;
754 }
755 
GetCalendar(napi_env env,napi_callback_info info)756 napi_value IntlAddon::GetCalendar(napi_env env, napi_callback_info info)
757 {
758     size_t argc = 0;
759     napi_value argv[0];
760     napi_value thisVar = nullptr;
761     void *data = nullptr;
762     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
763 
764     IntlAddon *obj = nullptr;
765     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
766     if (status != napi_ok || !obj || !obj->locale_) {
767         HiLog::Error(LABEL, "Get Locale object failed");
768         return nullptr;
769     }
770     std::string value = obj->locale_->GetCalendar();
771 
772     napi_value result = nullptr;
773     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
774     if (status != napi_ok) {
775         HiLog::Error(LABEL, "Create base name string failed");
776         return nullptr;
777     }
778     return result;
779 }
780 
GetCollation(napi_env env,napi_callback_info info)781 napi_value IntlAddon::GetCollation(napi_env env, napi_callback_info info)
782 {
783     size_t argc = 0;
784     napi_value argv[0];
785     napi_value thisVar = nullptr;
786     void *data = nullptr;
787     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
788 
789     IntlAddon *obj = nullptr;
790     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
791     if (status != napi_ok || !obj || !obj->locale_) {
792         HiLog::Error(LABEL, "Get Locale object failed");
793         return nullptr;
794     }
795     std::string value = obj->locale_->GetCollation();
796 
797     napi_value result = nullptr;
798     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
799     if (status != napi_ok) {
800         HiLog::Error(LABEL, "Create base name string failed");
801         return nullptr;
802     }
803     return result;
804 }
805 
GetHourCycle(napi_env env,napi_callback_info info)806 napi_value IntlAddon::GetHourCycle(napi_env env, napi_callback_info info)
807 {
808     size_t argc = 0;
809     napi_value argv[0];
810     napi_value thisVar = nullptr;
811     void *data = nullptr;
812     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
813 
814     IntlAddon *obj = nullptr;
815     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
816     if (status != napi_ok || !obj || !obj->locale_) {
817         HiLog::Error(LABEL, "Get Locale object failed");
818         return nullptr;
819     }
820     std::string value = obj->locale_->GetHourCycle();
821 
822     napi_value result = nullptr;
823     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
824     if (status != napi_ok) {
825         HiLog::Error(LABEL, "Create base name string failed");
826         return nullptr;
827     }
828     return result;
829 }
830 
GetNumberingSystem(napi_env env,napi_callback_info info)831 napi_value IntlAddon::GetNumberingSystem(napi_env env, napi_callback_info info)
832 {
833     size_t argc = 0;
834     napi_value argv[0];
835     napi_value thisVar = nullptr;
836     void *data = nullptr;
837     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
838 
839     IntlAddon *obj = nullptr;
840     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
841     if (status != napi_ok || !obj || !obj->locale_) {
842         HiLog::Error(LABEL, "Get Locale object failed");
843         return nullptr;
844     }
845     std::string value = obj->locale_->GetNumberingSystem();
846 
847     napi_value result = nullptr;
848     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
849     if (status != napi_ok) {
850         HiLog::Error(LABEL, "Create base name string failed");
851         return nullptr;
852     }
853     return result;
854 }
855 
GetNumeric(napi_env env,napi_callback_info info)856 napi_value IntlAddon::GetNumeric(napi_env env, napi_callback_info info)
857 {
858     size_t argc = 0;
859     napi_value argv[0];
860     napi_value thisVar = nullptr;
861     void *data = nullptr;
862     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
863 
864     IntlAddon *obj = nullptr;
865     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
866     if (status != napi_ok || !obj || !obj->locale_) {
867         HiLog::Error(LABEL, "Get Locale object failed");
868         return nullptr;
869     }
870     std::string value = obj->locale_->GetNumeric();
871     bool optionBoolValue = (value == "true");
872     napi_value result = nullptr;
873     status = napi_get_boolean(env, optionBoolValue, &result);
874     if (status != napi_ok) {
875         HiLog::Error(LABEL, "Create numeric boolean value failed");
876         return nullptr;
877     }
878     return result;
879 }
880 
GetCaseFirst(napi_env env,napi_callback_info info)881 napi_value IntlAddon::GetCaseFirst(napi_env env, napi_callback_info info)
882 {
883     size_t argc = 0;
884     napi_value argv[0];
885     napi_value thisVar = nullptr;
886     void *data = nullptr;
887     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
888 
889     IntlAddon *obj = nullptr;
890     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
891     if (status != napi_ok || !obj || !obj->locale_) {
892         HiLog::Error(LABEL, "Get Locale object failed");
893         return nullptr;
894     }
895     std::string value = obj->locale_->GetCaseFirst();
896     napi_value result = nullptr;
897     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
898     if (status != napi_ok) {
899         HiLog::Error(LABEL, "Create caseFirst string failed");
900         return nullptr;
901     }
902     return result;
903 }
904 
ToString(napi_env env,napi_callback_info info)905 napi_value IntlAddon::ToString(napi_env env, napi_callback_info info)
906 {
907     size_t argc = 0;
908     napi_value argv[0];
909     napi_value thisVar = nullptr;
910     void *data = nullptr;
911     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
912 
913     IntlAddon *obj = nullptr;
914     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
915     if (status != napi_ok || !obj || !obj->locale_) {
916         HiLog::Error(LABEL, "Get Locale object failed");
917         return nullptr;
918     }
919     std::string value = obj->locale_->ToString();
920 
921     napi_value result = nullptr;
922     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
923     if (status != napi_ok) {
924         HiLog::Error(LABEL, "Create language string failed");
925         return nullptr;
926     }
927     return result;
928 }
929 
Maximize(napi_env env,napi_callback_info info)930 napi_value IntlAddon::Maximize(napi_env env, napi_callback_info info)
931 {
932     size_t argc = 0;
933     napi_value argv[0];
934     napi_value thisVar = nullptr;
935     void *data = nullptr;
936     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
937 
938     IntlAddon *obj = nullptr;
939     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
940     if (status != napi_ok || !obj || !obj->locale_) {
941         HiLog::Error(LABEL, "Get Locale object failed");
942         return nullptr;
943     }
944     std::string localeTag = obj->locale_->Maximize();
945 
946     napi_value constructor = nullptr;
947     status = napi_get_reference_value(env, *g_constructor, &constructor);
948     if (status != napi_ok) {
949         HiLog::Error(LABEL, "Get locale constructor reference failed");
950         return nullptr;
951     }
952     napi_value result = nullptr;
953     napi_value arg = nullptr;
954     status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg);
955     if (status != napi_ok) {
956         HiLog::Error(LABEL, "Create localeTag string failed");
957         return nullptr;
958     }
959     status = napi_new_instance(env, constructor, 1, &arg, &result);
960     if (status != napi_ok) {
961         HiLog::Error(LABEL, "Create new locale instance failed");
962         return nullptr;
963     }
964     return result;
965 }
966 
Minimize(napi_env env,napi_callback_info info)967 napi_value IntlAddon::Minimize(napi_env env, napi_callback_info info)
968 {
969     size_t argc = 0;
970     napi_value argv[0];
971     napi_value thisVar = nullptr;
972     void *data = nullptr;
973     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
974 
975     IntlAddon *obj = nullptr;
976     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
977     if (status != napi_ok || !obj || !obj->locale_) {
978         HiLog::Error(LABEL, "Get Locale object failed");
979         return nullptr;
980     }
981     std::string localeTag = obj->locale_->Minimize();
982 
983     napi_value constructor = nullptr;
984     status = napi_get_reference_value(env, *g_constructor, &constructor);
985     if (status != napi_ok) {
986         HiLog::Error(LABEL, "Get locale constructor reference failed");
987         return nullptr;
988     }
989     napi_value result = nullptr;
990     napi_value arg = nullptr;
991     status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg);
992     if (status != napi_ok) {
993         HiLog::Error(LABEL, "Create localeTag string failed");
994         return nullptr;
995     }
996     status = napi_new_instance(env, constructor, 1, &arg, &result);
997     if (status != napi_ok) {
998         HiLog::Error(LABEL, "Create new locale instance failed");
999         return nullptr;
1000     }
1001     return result;
1002 }
1003 
SetOptionProperties(napi_env env,napi_value & result,std::map<std::string,std::string> & options,const std::string & option)1004 void SetOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
1005     const std::string &option)
1006 {
1007     if (options.count(option) > 0) {
1008         std::string optionValue = options[option];
1009         napi_value optionJsValue = nullptr;
1010         napi_create_string_utf8(env, optionValue.c_str(), NAPI_AUTO_LENGTH, &optionJsValue);
1011         napi_set_named_property(env, result, option.c_str(), optionJsValue);
1012     } else {
1013         napi_value undefined = nullptr;
1014         napi_get_undefined(env, &undefined);
1015         napi_set_named_property(env, result, option.c_str(), undefined);
1016     }
1017 }
1018 
SetIntegerOptionProperties(napi_env env,napi_value & result,std::map<std::string,std::string> & options,const std::string & option)1019 void SetIntegerOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
1020     const std::string &option)
1021 {
1022     if (options.count(option) > 0) {
1023         std::string optionValue = options[option];
1024         napi_value optionJsValue = nullptr;
1025         int64_t integerValue = std::stoi(optionValue);
1026         napi_create_int64(env, integerValue, &optionJsValue);
1027         napi_set_named_property(env, result, option.c_str(), optionJsValue);
1028     } else {
1029         napi_value undefined = nullptr;
1030         napi_get_undefined(env, &undefined);
1031         napi_set_named_property(env, result, option.c_str(), undefined);
1032     }
1033 }
1034 
SetBooleanOptionProperties(napi_env env,napi_value & result,std::map<std::string,std::string> & options,const std::string & option)1035 void SetBooleanOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
1036     const std::string &option)
1037 {
1038     if (options.count(option) > 0) {
1039         std::string optionValue = options[option];
1040         bool optionBoolValue = (optionValue == "true");
1041         napi_value optionJsValue = nullptr;
1042         napi_get_boolean(env, optionBoolValue, &optionJsValue);
1043         napi_set_named_property(env, result, option.c_str(), optionJsValue);
1044     } else {
1045         napi_value undefined = nullptr;
1046         napi_get_undefined(env, &undefined);
1047         napi_set_named_property(env, result, option.c_str(), undefined);
1048     }
1049 }
1050 
GetRelativeTimeResolvedOptions(napi_env env,napi_callback_info info)1051 napi_value IntlAddon::GetRelativeTimeResolvedOptions(napi_env env, napi_callback_info info)
1052 {
1053     size_t argc = 0;
1054     napi_value argv[0];
1055     napi_value thisVar = nullptr;
1056     void *data = nullptr;
1057     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1058 
1059     IntlAddon *obj = nullptr;
1060     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1061     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
1062         HiLog::Error(LABEL, "Get RelativeTimeFormat object failed");
1063         return nullptr;
1064     }
1065     napi_value result = nullptr;
1066     napi_create_object(env, &result);
1067     std::map<std::string, std::string> options = {};
1068     obj->relativetimefmt_->GetResolvedOptions(options);
1069     SetOptionProperties(env, result, options, "locale");
1070     SetOptionProperties(env, result, options, "style");
1071     SetOptionProperties(env, result, options, "numeric");
1072     SetOptionProperties(env, result, options, "numberingSystem");
1073     return result;
1074 }
1075 
GetDateTimeResolvedOptions(napi_env env,napi_callback_info info)1076 napi_value IntlAddon::GetDateTimeResolvedOptions(napi_env env, napi_callback_info info)
1077 {
1078     size_t argc = 0;
1079     napi_value argv[0];
1080     napi_value thisVar = nullptr;
1081     void *data = nullptr;
1082     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1083 
1084     IntlAddon *obj = nullptr;
1085     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1086     if (status != napi_ok || !obj || !obj->datefmt_) {
1087         HiLog::Error(LABEL, "Get DateTimeFormat object failed");
1088         return nullptr;
1089     }
1090     napi_value result = nullptr;
1091     napi_create_object(env, &result);
1092     std::map<std::string, std::string> options = {};
1093     obj->datefmt_->GetResolvedOptions(options);
1094     SetOptionProperties(env, result, options, "locale");
1095     SetOptionProperties(env, result, options, "calendar");
1096     SetOptionProperties(env, result, options, "dateStyle");
1097     SetOptionProperties(env, result, options, "timeStyle");
1098     SetOptionProperties(env, result, options, "hourCycle");
1099     SetOptionProperties(env, result, options, "timeZone");
1100     SetOptionProperties(env, result, options, "timeZoneName");
1101     SetOptionProperties(env, result, options, "numberingSystem");
1102     SetBooleanOptionProperties(env, result, options, "hour12");
1103     SetOptionProperties(env, result, options, "weekday");
1104     SetOptionProperties(env, result, options, "era");
1105     SetOptionProperties(env, result, options, "year");
1106     SetOptionProperties(env, result, options, "month");
1107     SetOptionProperties(env, result, options, "day");
1108     SetOptionProperties(env, result, options, "hour");
1109     SetOptionProperties(env, result, options, "minute");
1110     SetOptionProperties(env, result, options, "second");
1111     SetOptionProperties(env, result, options, "dayPeriod");
1112     SetOptionProperties(env, result, options, "localeMatcher");
1113     SetOptionProperties(env, result, options, "formatMatcher");
1114     return result;
1115 }
1116 
GetNumberResolvedOptions(napi_env env,napi_callback_info info)1117 napi_value IntlAddon::GetNumberResolvedOptions(napi_env env, napi_callback_info info)
1118 {
1119     size_t argc = 0;
1120     napi_value argv[0];
1121     napi_value thisVar = nullptr;
1122     void *data = nullptr;
1123     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1124 
1125     IntlAddon *obj = nullptr;
1126     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1127     if (status != napi_ok || !obj || !obj->numberfmt_) {
1128         HiLog::Error(LABEL, "Get NumberFormat object failed");
1129         return nullptr;
1130     }
1131     napi_value result = nullptr;
1132     napi_create_object(env, &result);
1133     std::map<std::string, std::string> options = {};
1134     obj->numberfmt_->GetResolvedOptions(options);
1135     SetOptionProperties(env, result, options, "locale");
1136     SetOptionProperties(env, result, options, "currency");
1137     SetOptionProperties(env, result, options, "currencySign");
1138     SetOptionProperties(env, result, options, "currencyDisplay");
1139     SetOptionProperties(env, result, options, "unit");
1140     SetOptionProperties(env, result, options, "unitDisplay");
1141     SetOptionProperties(env, result, options, "signDisplay");
1142     SetOptionProperties(env, result, options, "compactDisplay");
1143     SetOptionProperties(env, result, options, "notation");
1144     SetOptionProperties(env, result, options, "style");
1145     SetOptionProperties(env, result, options, "numberingSystem");
1146     SetOptionProperties(env, result, options, "unitUsage");
1147     SetBooleanOptionProperties(env, result, options, "useGrouping");
1148     SetIntegerOptionProperties(env, result, options, "minimumIntegerDigits");
1149     SetIntegerOptionProperties(env, result, options, "minimumFractionDigits");
1150     SetIntegerOptionProperties(env, result, options, "maximumFractionDigits");
1151     SetIntegerOptionProperties(env, result, options, "minimumSignificantDigits");
1152     SetIntegerOptionProperties(env, result, options, "maximumSignificantDigits");
1153     SetOptionProperties(env, result, options, "localeMatcher");
1154     return result;
1155 }
1156 
FormatNumber(napi_env env,napi_callback_info info)1157 napi_value IntlAddon::FormatNumber(napi_env env, napi_callback_info info)
1158 {
1159     size_t argc = 1;
1160     napi_value argv[1] = { 0 };
1161     napi_value thisVar = nullptr;
1162     void *data = nullptr;
1163     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1164     double number = 0;
1165     napi_get_value_double(env, argv[0], &number);
1166     IntlAddon *obj = nullptr;
1167     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1168     if (status != napi_ok || !obj || !obj->numberfmt_) {
1169         HiLog::Error(LABEL, "Get NumberFormat object failed");
1170         return nullptr;
1171     }
1172     std::string value = obj->numberfmt_->Format(number);
1173     napi_value result = nullptr;
1174     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
1175     if (status != napi_ok) {
1176         HiLog::Error(LABEL, "Create format string failed");
1177         return nullptr;
1178     }
1179     return result;
1180 }
1181 
GetCollatorLocaleMatcher(napi_env env,napi_value options,std::map<std::string,std::string> & map)1182 void GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1183 {
1184     GetOptionValue(env, options, "localeMatcher", map);
1185     auto it = map.find("localeMatcher");
1186     if (it != map.end()) {
1187         std::string localeMatcher = it->second;
1188         if (localeMatcher != "lookup" && localeMatcher != "best fit") {
1189             HiLog::Error(LABEL, "invalid localeMatcher");
1190             return;
1191         }
1192     } else {
1193         map.insert(std::make_pair("localeMatcher", "best fit"));
1194     }
1195 }
1196 
GetCollatorUsage(napi_env env,napi_value options,std::map<std::string,std::string> & map)1197 void GetCollatorUsage(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1198 {
1199     GetOptionValue(env, options, "usage", map);
1200     auto it = map.find("usage");
1201     if (it != map.end()) {
1202         std::string usage = it->second;
1203         if (usage != "sort" && usage != "search") {
1204             HiLog::Error(LABEL, "invalid usage");
1205             return;
1206         }
1207     } else {
1208         map.insert(std::make_pair("usage", "sort"));
1209     }
1210 }
1211 
GetCollatorSensitivity(napi_env env,napi_value options,std::map<std::string,std::string> & map)1212 void GetCollatorSensitivity(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1213 {
1214     GetOptionValue(env, options, "sensitivity", map);
1215     auto it = map.find("sensitivity");
1216     if (it != map.end()) {
1217         std::string sensitivity = it->second;
1218         if (sensitivity != "base" && sensitivity != "accent" && sensitivity != "case" && sensitivity != "variant") {
1219             HiLog::Error(LABEL, "invalid sensitivity");
1220             return;
1221         }
1222     } else {
1223         map.insert(std::make_pair("sensitivity", "variant"));
1224     }
1225 }
1226 
GetCollatorIgnorePunctuation(napi_env env,napi_value options,std::map<std::string,std::string> & map)1227 void GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1228 {
1229     GetBoolOptionValue(env, options, "ignorePunctuation", map);
1230     auto it = map.find("ignorePunctuation");
1231     if (it != map.end()) {
1232         std::string ignorePunctuation = it->second;
1233         if (ignorePunctuation != "true" && ignorePunctuation != "false") {
1234             HiLog::Error(LABEL, "invalid ignorePunctuation");
1235             return;
1236         }
1237     } else {
1238         map.insert(std::make_pair("ignorePunctuation", "false"));
1239     }
1240 }
1241 
GetCollatorNumeric(napi_env env,napi_value options,std::map<std::string,std::string> & map)1242 void GetCollatorNumeric(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1243 {
1244     GetBoolOptionValue(env, options, "numeric", map);
1245     auto it = map.find("numeric");
1246     if (it != map.end()) {
1247         std::string numeric = it->second;
1248         if (numeric != "true" && numeric != "false") {
1249             HiLog::Error(LABEL, "invalid numeric");
1250             return;
1251         }
1252     }
1253 }
1254 
GetCollatorCaseFirst(napi_env env,napi_value options,std::map<std::string,std::string> & map)1255 void GetCollatorCaseFirst(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1256 {
1257     GetOptionValue(env, options, "caseFirst", map);
1258     auto it = map.find("caseFirst");
1259     if (it != map.end()) {
1260         std::string caseFirst = it->second;
1261         if (caseFirst != "upper" && caseFirst != "lower" && caseFirst != "false") {
1262             HiLog::Error(LABEL, "invalid caseFirst");
1263             return;
1264         }
1265     }
1266 }
1267 
GetCollatorCollation(napi_env env,napi_value options,std::map<std::string,std::string> & map)1268 void GetCollatorCollation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1269 {
1270     GetOptionValue(env, options, "collation", map);
1271     auto it = map.find("collation");
1272     if (it != map.end()) {
1273         std::string collation = it->second;
1274         std::set<std::string> validCollation;
1275         validCollation.insert("big5han");
1276         validCollation.insert("compat");
1277         validCollation.insert("dict");
1278         validCollation.insert("direct");
1279         validCollation.insert("ducet");
1280         validCollation.insert("eor");
1281         validCollation.insert("gb2312");
1282         validCollation.insert("phonebk");
1283         validCollation.insert("phonetic");
1284         validCollation.insert("pinyin");
1285         validCollation.insert("reformed");
1286         validCollation.insert("searchjl");
1287         validCollation.insert("stroke");
1288         validCollation.insert("trad");
1289         validCollation.insert("unihan");
1290         validCollation.insert("zhuyin");
1291         if (validCollation.find(collation) == validCollation.end()) {
1292             map["collation"] = "default";
1293         }
1294     }
1295 }
1296 
GetCollatorOptionValue(napi_env env,napi_value options,std::map<std::string,std::string> & map)1297 void GetCollatorOptionValue(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1298 {
1299     GetCollatorLocaleMatcher(env, options, map);
1300     GetCollatorUsage(env, options, map);
1301     GetCollatorSensitivity(env, options, map);
1302     GetCollatorIgnorePunctuation(env, options, map);
1303     GetCollatorNumeric(env, options, map);
1304     GetCollatorCaseFirst(env, options, map);
1305     GetCollatorCollation(env, options, map);
1306 }
1307 
InitCollator(napi_env env,napi_value exports)1308 napi_value IntlAddon::InitCollator(napi_env env, napi_value exports)
1309 {
1310     napi_status status = napi_ok;
1311     napi_property_descriptor properties[] = {
1312         DECLARE_NAPI_FUNCTION("compare", CompareString),
1313         DECLARE_NAPI_FUNCTION("resolvedOptions", GetCollatorResolvedOptions)
1314     };
1315 
1316     napi_value constructor;
1317     status = napi_define_class(env, "Collator", NAPI_AUTO_LENGTH, CollatorConstructor, nullptr,
1318         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
1319     if (status != napi_ok) {
1320         HiLog::Error(LABEL, "Define class failed when InitCollator");
1321         return nullptr;
1322     }
1323 
1324     status = napi_set_named_property(env, exports, "Collator", constructor);
1325     if (status != napi_ok) {
1326         HiLog::Error(LABEL, "Set property failed when InitCollator");
1327         return nullptr;
1328     }
1329     return exports;
1330 }
1331 
CollatorConstructor(napi_env env,napi_callback_info info)1332 napi_value IntlAddon::CollatorConstructor(napi_env env, napi_callback_info info)
1333 {
1334     size_t argc = 2;
1335     napi_value argv[2] = { 0 };
1336     napi_value thisVar = nullptr;
1337     void *data = nullptr;
1338     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1339     if (status != napi_ok) {
1340         return nullptr;
1341     }
1342     std::vector<std::string> localeTags;
1343     if (argv[0] != nullptr) {
1344         napi_valuetype valueType = napi_valuetype::napi_undefined;
1345         napi_typeof(env, argv[0], &valueType);
1346         bool isArray = false;
1347         napi_is_array(env, argv[0], &isArray);
1348         if (valueType == napi_valuetype::napi_string) {
1349             GetLocaleTags(env, argv[0], localeTags);
1350         } else if (isArray) {
1351             uint32_t arrayLength = 0;
1352             napi_get_array_length(env, argv[0], &arrayLength);
1353             napi_value element = nullptr;
1354             for (uint32_t i = 0; i < arrayLength; i++) {
1355                 napi_get_element(env, argv[0], i, &element);
1356                 GetLocaleTags(env, element, localeTags);
1357             }
1358         }
1359     }
1360     std::map<std::string, std::string> map = {};
1361     if (argv[1] != nullptr) {
1362         GetCollatorOptionValue(env, argv[1], map);
1363     }
1364     std::unique_ptr<IntlAddon> obj = nullptr;
1365     obj = std::make_unique<IntlAddon>();
1366     status =
1367         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
1368     if (status != napi_ok) {
1369         HiLog::Error(LABEL, "Wrap IntlAddon failed");
1370         return nullptr;
1371     }
1372     if (!obj->InitCollatorContext(env, info, localeTags, map)) {
1373         HiLog::Error(LABEL, "Init DateTimeFormat failed");
1374         return nullptr;
1375     }
1376     obj.release();
1377     return thisVar;
1378 }
1379 
InitCollatorContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)1380 bool IntlAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
1381     std::map<std::string, std::string> &map)
1382 {
1383     napi_value global = nullptr;
1384     napi_status status = napi_get_global(env, &global);
1385     if (status != napi_ok) {
1386         HiLog::Error(LABEL, "Get global failed");
1387         return false;
1388     }
1389     env_ = env;
1390     collator_ = std::make_unique<Collator>(localeTags, map);
1391 
1392     return collator_ != nullptr;
1393 }
1394 
GetStringParameter(napi_env env,napi_value value,std::vector<char> & buf)1395 bool GetStringParameter(napi_env env, napi_value value, std::vector<char> &buf)
1396 {
1397     napi_valuetype valueType = napi_valuetype::napi_undefined;
1398     napi_typeof(env, value, &valueType);
1399     if (valueType != napi_valuetype::napi_string) {
1400         napi_throw_type_error(env, nullptr, "Parameter type does not match");
1401         return false;
1402     }
1403     size_t len = 0;
1404     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
1405     if (status != napi_ok) {
1406         HiLog::Error(LABEL, "Get first length failed");
1407         return false;
1408     }
1409     buf.resize(len + 1);
1410     status = napi_get_value_string_utf8(env, value, buf.data(), len + 1, &len);
1411     if (status != napi_ok) {
1412         HiLog::Error(LABEL, "Get first failed");
1413         return false;
1414     }
1415 
1416     return true;
1417 }
1418 
FormatRelativeTime(napi_env env,napi_callback_info info)1419 napi_value IntlAddon::FormatRelativeTime(napi_env env, napi_callback_info info)
1420 {
1421     size_t argc = 2;
1422     napi_value argv[2] = { 0 };
1423     napi_value thisVar = nullptr;
1424     void *data = nullptr;
1425     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1426     napi_status status;
1427     double number;
1428     status = napi_get_value_double(env, argv[0], &number);
1429     if (status != napi_ok) {
1430         HiLog::Error(LABEL, "Get number failed");
1431         return nullptr;
1432     }
1433     std::vector<char> unit;
1434     if (!GetStringParameter(env, argv[1], unit)) {
1435         return nullptr;
1436     }
1437     IntlAddon *obj = nullptr;
1438     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1439     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
1440         HiLog::Error(LABEL, "Get RelativeTimeFormat object failed");
1441         return nullptr;
1442     }
1443     std::string value = obj->relativetimefmt_->Format(number, unit.data());
1444     napi_value result = nullptr;
1445     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
1446     if (status != napi_ok) {
1447         HiLog::Error(LABEL, "Create format string failed");
1448         return nullptr;
1449     }
1450     return result;
1451 }
1452 
FillInArrayElement(napi_env env,napi_value & result,napi_status & status,const std::vector<std::vector<std::string>> & timeVector)1453 void IntlAddon::FillInArrayElement(napi_env env, napi_value &result, napi_status &status,
1454     const std::vector<std::vector<std::string>> &timeVector)
1455 {
1456     for (size_t i = 0; i < timeVector.size(); i++) {
1457         napi_value value = nullptr;
1458         status = napi_create_string_utf8(env, timeVector[i][1].c_str(), NAPI_AUTO_LENGTH, &value);
1459         if (status != napi_ok) {
1460             HiLog::Error(LABEL, "Failed to create string item.");
1461             return;
1462         }
1463         napi_value type = nullptr;
1464         status = napi_create_string_utf8(env, timeVector[i][0].c_str(), NAPI_AUTO_LENGTH, &type);
1465         if (status != napi_ok) {
1466             HiLog::Error(LABEL, "Failed to create string item.");
1467             return;
1468         }
1469         napi_value unit = nullptr;
1470         size_t unitIndex = 2;
1471         if (timeVector[i].size() > unitIndex) {
1472             status = napi_create_string_utf8(env, timeVector[i][unitIndex].c_str(), NAPI_AUTO_LENGTH, &unit);
1473             if (status != napi_ok) {
1474                 HiLog::Error(LABEL, "Failed to create string item.");
1475                 return;
1476             }
1477         } else {
1478             napi_get_undefined(env, &unit);
1479         }
1480         napi_value formatInfo;
1481         status = napi_create_object(env, &formatInfo);
1482         if (status != napi_ok) {
1483             HiLog::Error(LABEL, "Failed to create format info object.");
1484             return;
1485         }
1486         napi_set_named_property(env, formatInfo, "type", type);
1487         napi_set_named_property(env, formatInfo, "value", value);
1488         napi_set_named_property(env, formatInfo, "unit", unit);
1489         status = napi_set_element(env, result, i, formatInfo);
1490         if (status != napi_ok) {
1491             HiLog::Error(LABEL, "Failed to set array item");
1492             return;
1493         }
1494     }
1495 }
1496 
FormatToParts(napi_env env,napi_callback_info info)1497 napi_value IntlAddon::FormatToParts(napi_env env, napi_callback_info info)
1498 {
1499     size_t argc = 2;
1500     napi_value argv[2] = { 0 };
1501     napi_value thisVar = nullptr;
1502     void *data = nullptr;
1503     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1504     double number = 0;
1505     napi_get_value_double(env, argv[0], &number);
1506     std::vector<char> unit;
1507     if (!GetStringParameter(env, argv[1], unit)) {
1508         return nullptr;
1509     }
1510     IntlAddon *obj = nullptr;
1511     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1512     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
1513         HiLog::Error(LABEL, "Get RelativeTimeFormat object failed");
1514         return nullptr;
1515     }
1516     std::vector<std::vector<std::string>> timeVector;
1517     obj->relativetimefmt_->FormatToParts(number, unit.data(), timeVector);
1518     napi_value result = nullptr;
1519     status = napi_create_array_with_length(env, timeVector.size(), &result);
1520     if (status != napi_ok) {
1521         HiLog::Error(LABEL, "Failed to create array");
1522         return nullptr;
1523     }
1524     FillInArrayElement(env, result, status, timeVector);
1525     return result;
1526 }
1527 
CompareString(napi_env env,napi_callback_info info)1528 napi_value IntlAddon::CompareString(napi_env env, napi_callback_info info)
1529 {
1530     size_t argc = 2;
1531     napi_value argv[2] = { 0 };
1532     napi_value thisVar = nullptr;
1533     void *data = nullptr;
1534     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1535 
1536     std::vector<char> first;
1537     if (!GetStringParameter(env, argv[0], first)) {
1538         return nullptr;
1539     }
1540 
1541     std::vector<char> second;
1542     if (!GetStringParameter(env, argv[1], second)) {
1543         return nullptr;
1544     }
1545 
1546     IntlAddon *obj = nullptr;
1547     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1548     if (status != napi_ok || !obj || !obj->collator_) {
1549         HiLog::Error(LABEL, "Get Collator object failed");
1550         return nullptr;
1551     }
1552 
1553     CompareResult compareResult = obj->collator_->Compare(first.data(), second.data());
1554     napi_value result = nullptr;
1555     status = napi_create_int32(env, compareResult, &result);
1556     if (status != napi_ok) {
1557         HiLog::Error(LABEL, "Create compare result failed");
1558         return nullptr;
1559     }
1560 
1561     return result;
1562 }
1563 
GetCollatorResolvedOptions(napi_env env,napi_callback_info info)1564 napi_value IntlAddon::GetCollatorResolvedOptions(napi_env env, napi_callback_info info)
1565 {
1566     size_t argc = 0;
1567     napi_value argv[0];
1568     napi_value thisVar = nullptr;
1569     void *data = nullptr;
1570     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1571 
1572     IntlAddon *obj = nullptr;
1573     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1574     if (status != napi_ok || !obj || !obj->collator_) {
1575         HiLog::Error(LABEL, "Get Collator object failed");
1576         return nullptr;
1577     }
1578     napi_value result = nullptr;
1579     napi_create_object(env, &result);
1580     std::map<std::string, std::string> options = {};
1581     obj->collator_->ResolvedOptions(options);
1582     SetOptionProperties(env, result, options, "localeMatcher");
1583     SetOptionProperties(env, result, options, "locale");
1584     SetOptionProperties(env, result, options, "usage");
1585     SetOptionProperties(env, result, options, "sensitivity");
1586     SetBooleanOptionProperties(env, result, options, "ignorePunctuation");
1587     SetBooleanOptionProperties(env, result, options, "numeric");
1588     SetOptionProperties(env, result, options, "caseFirst");
1589     SetOptionProperties(env, result, options, "collation");
1590     return result;
1591 }
1592 
GetPluralRulesType(napi_env env,napi_value options,std::map<std::string,std::string> & map)1593 void GetPluralRulesType(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1594 {
1595     GetOptionValue(env, options, "type", map);
1596     auto it = map.find("type");
1597     if (it != map.end()) {
1598         std::string type = it->second;
1599         if (type != "cardinal" && type != "ordinal") {
1600             HiLog::Error(LABEL, "invalid type");
1601             return;
1602         }
1603     } else {
1604         map.insert(std::make_pair("type", "cardinal"));
1605     }
1606 }
1607 
GetPluralRulesInteger(napi_env env,napi_value options,std::map<std::string,std::string> & map)1608 void GetPluralRulesInteger(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1609 {
1610     GetIntegerOptionValue(env, options, "minimumIntegerDigits", map);
1611     auto it = map.find("minimumIntegerDigits");
1612     if (it != map.end()) {
1613         std::string minimumIntegerDigits = it->second;
1614         int n = std::stoi(minimumIntegerDigits);
1615         if (n < 1 || n > 21) {  // the valid range of minimumIntegerDigits is [1, 21]
1616             HiLog::Error(LABEL, "invalid minimumIntegerDigits");
1617             return;
1618         }
1619     } else {
1620         map.insert(std::make_pair("minimumIntegerDigits", std::to_string(1)));
1621     }
1622 }
1623 
GetPluralRulesFractions(napi_env env,napi_value options,std::map<std::string,std::string> & map)1624 void GetPluralRulesFractions(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1625 {
1626     GetIntegerOptionValue(env, options, "minimumFractionDigits", map);
1627     auto it = map.find("minimumFractionDigits");
1628     if (it != map.end()) {
1629         std::string minimumFractionDigits = it->second;
1630         int n = std::stoi(minimumFractionDigits);
1631         if (n < 0 || n > 20) {  // the valid range of minimumFractionDigits is [0, 20]
1632             HiLog::Error(LABEL, "invalid minimumFractionDigits");
1633             return;
1634         }
1635     }
1636 
1637     GetIntegerOptionValue(env, options, "maximumFractionDigits", map);
1638     it = map.find("maximumFractionDigits");
1639     if (it != map.end()) {
1640         std::string maximumFractionDigits = it->second;
1641         int n = std::stoi(maximumFractionDigits);
1642         if (n < 0 || n > 20) {  // the valid range of maximumFractionDigits is [0, 20]
1643             HiLog::Error(LABEL, "invalid maximumFractionDigits");
1644             return;
1645         }
1646     }
1647 }
1648 
GetPluralRulesSignificant(napi_env env,napi_value options,std::map<std::string,std::string> & map)1649 void GetPluralRulesSignificant(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1650 {
1651     int minSignificant = -1;
1652     GetIntegerOptionValue(env, options, "minimumSignificantDigits", map);
1653     auto it = map.find("minimumSignificantDigits");
1654     if (it != map.end()) {
1655         std::string minSignificantStr = it->second;
1656         int minSignificantInt = std::stoi(minSignificantStr);
1657         // the valid range of minSignificantInt is [1, 21]
1658         if (minSignificantInt < 1 || minSignificantInt > 21) {
1659             HiLog::Error(LABEL, "invalid minimumSignificantDigits");
1660             return;
1661         }
1662         minSignificant = minSignificantInt;
1663     } else {
1664         minSignificant = 1;
1665     }
1666 
1667     GetIntegerOptionValue(env, options, "maximumSignificantDigits", map);
1668     it = map.find("maximumSignificantDigits");
1669     if (it != map.end()) {
1670         std::string maxSignificantStr = it->second;
1671         int maxSignificant = std::stoi(maxSignificantStr);
1672         // the valid range of minSignificant is [minSignificant, 21]
1673         if (maxSignificant < minSignificant || maxSignificant > 21) {
1674             HiLog::Error(LABEL, "invalid maximumSignificantDigits");
1675             return;
1676         }
1677     }
1678 }
1679 
GetPluralRulesOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)1680 void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1681 {
1682     GetCollatorLocaleMatcher(env, options, map);
1683     GetPluralRulesType(env, options, map);
1684     GetPluralRulesInteger(env, options, map);
1685     GetPluralRulesFractions(env, options, map);
1686     GetPluralRulesSignificant(env, options, map);
1687 }
1688 
InitPluralRules(napi_env env,napi_value exports)1689 napi_value IntlAddon::InitPluralRules(napi_env env, napi_value exports)
1690 {
1691     napi_status status = napi_ok;
1692     napi_property_descriptor properties[] = {
1693         DECLARE_NAPI_FUNCTION("select", Select)
1694     };
1695 
1696     napi_value constructor = nullptr;
1697     status = napi_define_class(env, "PluralRules", NAPI_AUTO_LENGTH, PluralRulesConstructor, nullptr,
1698         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
1699     if (status != napi_ok) {
1700         HiLog::Error(LABEL, "Define class failed when InitPluralRules");
1701         return nullptr;
1702     }
1703 
1704     status = napi_set_named_property(env, exports, "PluralRules", constructor);
1705     if (status != napi_ok) {
1706         HiLog::Error(LABEL, "Set property failed when InitPluralRules");
1707         return nullptr;
1708     }
1709     return exports;
1710 }
1711 
PluralRulesConstructor(napi_env env,napi_callback_info info)1712 napi_value IntlAddon::PluralRulesConstructor(napi_env env, napi_callback_info info)
1713 {
1714     size_t argc = 2;
1715     napi_value argv[2] = { 0 };
1716     napi_value thisVar = nullptr;
1717     void *data = nullptr;
1718     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1719     if (status != napi_ok) {
1720         return nullptr;
1721     }
1722     napi_valuetype valueType = napi_valuetype::napi_undefined;
1723     std::vector<std::string> localeTags;
1724     if (argv[0] != nullptr) {
1725         napi_typeof(env, argv[0], &valueType);
1726         bool isArray = false;
1727         napi_is_array(env, argv[0], &isArray);
1728         if (valueType == napi_valuetype::napi_string) {
1729             GetLocaleTags(env, argv[0], localeTags);
1730         } else if (isArray) {
1731             uint32_t arrayLength = 0;
1732             napi_get_array_length(env, argv[0], &arrayLength);
1733             napi_value element = nullptr;
1734             for (uint32_t i = 0; i < arrayLength; i++) {
1735                 napi_get_element(env, argv[0], i, &element);
1736                 GetLocaleTags(env, element, localeTags);
1737             }
1738         }
1739     }
1740     std::map<std::string, std::string> map = {};
1741     if (argv[1] != nullptr) {
1742         GetPluralRulesOptionValues(env, argv[1], map);
1743     }
1744     std::unique_ptr<IntlAddon> obj = nullptr;
1745     obj = std::make_unique<IntlAddon>();
1746     status =
1747         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
1748     if (status != napi_ok) {
1749         HiLog::Error(LABEL, "Wrap IntlAddon failed");
1750         return nullptr;
1751     }
1752     if (!obj->InitPluralRulesContext(env, info, localeTags, map)) {
1753         HiLog::Error(LABEL, "Init DateTimeFormat failed");
1754         return nullptr;
1755     }
1756     obj.release();
1757     return thisVar;
1758 }
1759 
InitPluralRulesContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)1760 bool IntlAddon::InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
1761     std::map<std::string, std::string> &map)
1762 {
1763     napi_value global = nullptr;
1764     napi_status status = napi_get_global(env, &global);
1765     if (status != napi_ok) {
1766         HiLog::Error(LABEL, "Get global failed");
1767         return false;
1768     }
1769     env_ = env;
1770     pluralrules_ = std::make_unique<PluralRules>(localeTags, map);
1771 
1772     return pluralrules_ != nullptr;
1773 }
1774 
Select(napi_env env,napi_callback_info info)1775 napi_value IntlAddon::Select(napi_env env, napi_callback_info info)
1776 {
1777     size_t argc = 1;
1778     napi_value argv[1] = { 0 };
1779     napi_value thisVar = nullptr;
1780     void *data = nullptr;
1781     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1782     napi_valuetype valueType = napi_valuetype::napi_undefined;
1783     napi_typeof(env, argv[0], &valueType);
1784     if (valueType != napi_valuetype::napi_number) {
1785         napi_throw_type_error(env, nullptr, "Parameter type does not match");
1786         return nullptr;
1787     }
1788 
1789     double number = 0;
1790     napi_status status = napi_get_value_double(env, argv[0], &number);
1791     if (status != napi_ok) {
1792         HiLog::Error(LABEL, "Get number failed");
1793         return nullptr;
1794     }
1795 
1796     IntlAddon *obj = nullptr;
1797     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1798     if (status != napi_ok || !obj || !obj->pluralrules_) {
1799         HiLog::Error(LABEL, "Get PluralRules object failed");
1800         return nullptr;
1801     }
1802 
1803     std::string res = obj->pluralrules_->Select(number);
1804     napi_value result = nullptr;
1805     status = napi_create_string_utf8(env, res.c_str(), NAPI_AUTO_LENGTH, &result);
1806     if (status != napi_ok) {
1807         HiLog::Error(LABEL, "get select result failed");
1808         return nullptr;
1809     }
1810     return result;
1811 }
1812 
Init(napi_env env,napi_value exports)1813 napi_value Init(napi_env env, napi_value exports)
1814 {
1815     napi_value val = IntlAddon::InitLocale(env, exports);
1816     val = IntlAddon::InitDateTimeFormat(env, val);
1817     val = IntlAddon::InitNumberFormat(env, val);
1818     val = IntlAddon::InitCollator(env, val);
1819     val = IntlAddon::InitRelativeTimeFormat(env, val);
1820     return IntlAddon::InitPluralRules(env, val);
1821 }
1822 
1823 static napi_module g_intlModule = {
1824     .nm_version = 1,
1825     .nm_flags = 0,
1826     .nm_filename = nullptr,
1827     .nm_register_func = Init,
1828     .nm_modname = "intl",
1829     .nm_priv = nullptr,
1830     .reserved = { 0 }
1831 };
1832 
AbilityRegister()1833 extern "C" __attribute__((constructor)) void AbilityRegister()
1834 {
1835     napi_module_register(&g_intlModule);
1836 }
1837 } // namespace I18n
1838 } // namespace Global
1839 } // namespace OHOS
1840