• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 "styled_number_format_addon.h"
17 
18 #include "error_util.h"
19 #include "i18n_hilog.h"
20 #include "variable_convertor.h"
21 
22 namespace OHOS {
23 namespace Global {
24 namespace I18n {
StyledNumberFormatAddon()25 StyledNumberFormatAddon::StyledNumberFormatAddon()
26 {
27 }
28 
~StyledNumberFormatAddon()29 StyledNumberFormatAddon::~StyledNumberFormatAddon()
30 {
31     for (auto iter = styledNumberFormatOptions_.begin(); iter != styledNumberFormatOptions_.end(); ++iter) {
32         napi_delete_reference(env_, iter->second);
33     }
34 }
35 
Destructor(napi_env env,void * nativeObject,void * hint)36 void StyledNumberFormatAddon::Destructor(napi_env env, void *nativeObject, void *hint)
37 {
38     if (!nativeObject) {
39         return;
40     }
41     delete reinterpret_cast<StyledNumberFormatAddon *>(nativeObject);
42     nativeObject = nullptr;
43 }
44 
InitStyledNumberFormat(napi_env env,napi_value exports)45 napi_value StyledNumberFormatAddon::InitStyledNumberFormat(napi_env env, napi_value exports)
46 {
47     napi_property_descriptor properties[] = {
48         DECLARE_NAPI_FUNCTION("format", Format)
49     };
50     napi_value styledNumberFormatConstructor = nullptr;
51     napi_status status = napi_define_class(env, "StyledNumberFormat", NAPI_AUTO_LENGTH, constructor, nullptr,
52         sizeof(properties) / sizeof(napi_property_descriptor), properties, &styledNumberFormatConstructor);
53     if (status != napi_ok) {
54         HILOG_ERROR_I18N("Failed to define class StyledNumberFormat at Init.");
55         return nullptr;
56     }
57     status = napi_set_named_property(env, exports, "StyledNumberFormat", styledNumberFormatConstructor);
58     if (status != napi_ok) {
59         HILOG_ERROR_I18N("Set property failed when InitStyledNumberFormat");
60         return nullptr;
61     }
62     return exports;
63 }
64 
constructor(napi_env env,napi_callback_info info)65 napi_value StyledNumberFormatAddon::constructor(napi_env env, napi_callback_info info)
66 {
67     size_t argc = 2;
68     napi_value argv[2] = { nullptr };
69     napi_value thisVar = nullptr;
70     void *data = nullptr;
71     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
72     if (status != napi_ok) {
73         HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor get callback info error");
74         return nullptr;
75     }
76     if (argc < 1) {
77         ErrorUtil::NapiNotFoundError(env, I18N_NOT_FOUND, "numberFormat", true);
78         return nullptr;
79     }
80     NumberFormatAddon *numberFormatAddon = nullptr;
81     JSNumberFormatAddon *jsNumberFormatAddon = nullptr;
82     SimpleNumberFormatAddon *simpleNumberFormatAddon = nullptr;
83     GetNumberFmtOrSimpleNumberFmt(env, argv[0], &jsNumberFormatAddon, &simpleNumberFormatAddon, &numberFormatAddon);
84     if (!numberFormatAddon && !simpleNumberFormatAddon && !jsNumberFormatAddon) {
85         ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "numberFormat",
86             "intl.NumberFormat or SimpleNumberFormat or Intl.NumberFormat", true);
87         return nullptr;
88     }
89     std::unique_ptr<StyledNumberFormatAddon> obj = std::make_unique<StyledNumberFormatAddon>();
90     if (!obj) {
91         HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor make unique ptr error");
92         return nullptr;
93     }
94     status = napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), Destructor, nullptr, nullptr);
95     if (status != napi_ok) {
96         HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor napi_wrap error");
97         return nullptr;
98     }
99     if (!obj->InitStyledNumberFormatContent(env, jsNumberFormatAddon, simpleNumberFormatAddon,
100         numberFormatAddon, argv[1])) {
101         return nullptr;
102     }
103     obj.release();
104     return thisVar;
105 }
106 
Format(napi_env env,napi_callback_info info)107 napi_value StyledNumberFormatAddon::Format(napi_env env, napi_callback_info info)
108 {
109     size_t argc = 1;
110     napi_value argv[1] = { 0 };
111     napi_value thisVar = nullptr;
112     void *data = nullptr;
113     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
114     if (status != napi_ok) {
115         HILOG_ERROR_I18N("StyledNumberFormatAddon::Format: Get cb info failed.");
116         return CreateStyledString(env, "", nullptr);
117     } else if (argc < 1) {
118         ErrorUtil::NapiNotFoundError(env, I18N_NOT_FOUND, "value", true);
119         return CreateStyledString(env, "", nullptr);
120     }
121 
122     napi_valuetype valueType = napi_valuetype::napi_undefined;
123     status = napi_typeof(env, argv[0], &valueType);
124     if (status != napi_ok) {
125         HILOG_ERROR_I18N("StyledNumberFormatAddon::Format: Failed to get type of argv[0].");
126         return CreateStyledString(env, "", nullptr);
127     } else if (valueType != napi_valuetype::napi_number) {
128         ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "value", "number", true);
129         return CreateStyledString(env, "", nullptr);
130     }
131 
132     double number = 0;
133     status = napi_get_value_double(env, argv[0], &number);
134     if (status != napi_ok) {
135         HILOG_ERROR_I18N("Format: Get double from napi failed");
136         return CreateStyledString(env, "", nullptr);
137     }
138     StyledNumberFormatAddon *obj = nullptr;
139     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
140     if (status != napi_ok || !obj || !obj->styledNumberFormat_) {
141         HILOG_ERROR_I18N("Format: Get StyledNumberFormat object failed");
142         return CreateStyledString(env, "", nullptr);
143     }
144 
145     std::string formattedNumber = obj->styledNumberFormat_->Format(number);
146     std::vector<StyledNumberFormat::NumberPart> numberParts = obj->styledNumberFormat_->ParseToParts(number);
147 
148     napi_value styleOption = CreateStyleOptions(env, numberParts, obj->styledNumberFormatOptions_);
149 
150     napi_value styledString = CreateStyledString(env, formattedNumber, styleOption);
151     return styledString;
152 }
153 
InitStyledNumberFormatContent(napi_env env,JSNumberFormatAddon * jsNumberFormatAddon,SimpleNumberFormatAddon * simpleNumberFormatAddon,NumberFormatAddon * numberFormatAddon,napi_value options)154 bool StyledNumberFormatAddon::InitStyledNumberFormatContent(napi_env env, JSNumberFormatAddon* jsNumberFormatAddon,
155     SimpleNumberFormatAddon* simpleNumberFormatAddon, NumberFormatAddon *numberFormatAddon, napi_value options)
156 {
157     if (jsNumberFormatAddon) {
158         styledNumberFormat_ =
159             std::make_unique<StyledNumberFormat>(true, jsNumberFormatAddon->GetNumberFormat(), nullptr);
160     } else if (numberFormatAddon) {
161         styledNumberFormat_ =
162             std::make_unique<StyledNumberFormat>(true, numberFormatAddon->CopyNumberFormat(), nullptr);
163     } else if (simpleNumberFormatAddon) {
164         styledNumberFormat_ =
165             std::make_unique<StyledNumberFormat>(false, nullptr, simpleNumberFormatAddon->CopySimpleNumberFormat());
166     }
167 
168     if (options) {
169         SetTextStyle(env, options, "integer");
170         SetTextStyle(env, options, "decimal");
171         SetTextStyle(env, options, "fraction");
172         SetTextStyle(env, options, "unit");
173     }
174 
175     env_ = env;
176     return styledNumberFormat_ != nullptr;
177 }
178 
SetTextStyle(napi_env env,napi_value options,const std::string & optionName)179 void StyledNumberFormatAddon::SetTextStyle(napi_env env, napi_value options, const std::string &optionName)
180 {
181     napi_value optionValue = nullptr;
182     napi_valuetype type = napi_undefined;
183     napi_status status = napi_typeof(env, options, &type);
184     if (status != napi_ok && type != napi_object) {
185         HILOG_ERROR_I18N("setTextStyle: option is not an object");
186         return;
187     }
188     bool hasProperty = false;
189     status = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
190     if (status != napi_ok || !hasProperty) {
191         HILOG_ERROR_I18N("setTextStyle: Has not named %{public}s property", optionName.c_str());
192         return;
193     }
194     status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
195     if (status != napi_ok) {
196         HILOG_ERROR_I18N("setTextStyle: Get named property for %{public}s failed.", optionName.c_str());
197         return;
198     }
199 
200     styledNumberFormatOptions_[optionName] = nullptr;
201     if (!optionValue) {
202         HILOG_ERROR_I18N("setTextStyle: %{public}s is nullptr.", optionName.c_str());
203         return;
204     }
205     status = napi_create_reference(env, optionValue, 1, &styledNumberFormatOptions_[optionName]);
206     if (status != napi_ok) {
207         HILOG_ERROR_I18N("setTextStyle: create reference for %{public}s failed.", optionName.c_str());
208         styledNumberFormatOptions_[optionName] = nullptr;
209         return;
210     }
211 }
212 
CreateStyledString(napi_env env,const std::string & formattedNumber,napi_value styleOption)213 napi_value StyledNumberFormatAddon::CreateStyledString(napi_env env, const std::string &formattedNumber,
214     napi_value styleOption)
215 {
216     size_t argc = 2;    // StyledString need 2 args
217     napi_value argv[2] = {nullptr};
218     argv[0] = VariableConvertor::CreateString(env, formattedNumber);
219     argv[1] = styleOption;
220 
221     napi_value global = nullptr;
222     napi_status status = napi_get_global(env, &global);
223     if (status != napi_ok) {
224         HILOG_ERROR_I18N("CreateStyledString: get global object failed");
225         return nullptr;
226     }
227     napi_value constructor = nullptr;
228     status = napi_get_named_property(env, global, "StyledString", &constructor);
229     if (status != napi_ok) {
230         HILOG_ERROR_I18N("CreateStyledString: get StyledString failed");
231         return nullptr;
232     }
233 
234     napi_value styleString = nullptr;
235     status = napi_new_instance(env, constructor, argc, argv, &styleString);
236     if (status != napi_ok) {
237         HILOG_ERROR_I18N("CreateStyledString: napi_new_instance failed");
238         return nullptr;
239     }
240     return styleString;
241 }
242 
CreateStyleOptions(napi_env env,const std::vector<StyledNumberFormat::NumberPart> & numberParts,const std::unordered_map<std::string,napi_ref> & styledNumberFormatOptions)243 napi_value StyledNumberFormatAddon::CreateStyleOptions(napi_env env,
244     const std::vector<StyledNumberFormat::NumberPart> &numberParts,
245     const std::unordered_map<std::string, napi_ref> &styledNumberFormatOptions)
246 {
247     napi_value result = nullptr;
248     napi_status status = napi_create_array(env, &result);
249     if (status != napi_ok) {
250         HILOG_ERROR_I18N("CreateStyleOption: create array failed");
251         return nullptr;
252     }
253 
254     unsigned int index = 0;
255     for (size_t i = 0; i < numberParts.size(); ++i) {
256         if (styledNumberFormatOptions.find(numberParts[i].part_name) == styledNumberFormatOptions.end()) {
257             continue;
258         }
259         napi_ref textStyleRef = styledNumberFormatOptions.at(numberParts[i].part_name);
260         napi_value option = CreateStyleOption(env, numberParts[i], textStyleRef);
261         status = napi_set_element(env, result, index, option);
262         if (status != napi_ok) {
263             HILOG_ERROR_I18N("CreateStyleoption: set array %{public}u failed", index);
264             return nullptr;
265         }
266         index += 1;
267     }
268     return result;
269 }
270 
CreateStyleOption(napi_env env,StyledNumberFormat::NumberPart numberPart,napi_ref textStyleRef)271 napi_value StyledNumberFormatAddon::CreateStyleOption(napi_env env,
272     StyledNumberFormat::NumberPart numberPart, napi_ref textStyleRef)
273 {
274     if (!textStyleRef) {
275         HILOG_ERROR_I18N("CreateStyleoption: options do not have %{public}s",
276             numberPart.part_name.c_str());
277         return nullptr;
278     }
279     napi_value textStyle = nullptr;
280     napi_status status = napi_get_reference_value(env, textStyleRef, &textStyle);
281     if (status != napi_ok) {
282         HILOG_ERROR_I18N("CreateStyleoption: Failed to create reference at textStyle %{public}s",
283             numberPart.part_name.c_str());
284         return nullptr;
285     }
286     napi_value option = nullptr;
287     status = napi_create_object(env, &option);
288     if (status != napi_ok) {
289         HILOG_ERROR_I18N("CreateStyleoption: create object failed");
290         return nullptr;
291     }
292     napi_value start = nullptr;
293     status = napi_create_int32(env, numberPart.start, &start);
294     if (status != napi_ok) {
295         HILOG_ERROR_I18N("CreateStyleoption: create int32 failed");
296         return nullptr;
297     }
298     napi_value length = nullptr;
299     status = napi_create_int32(env, numberPart.length, &length);
300     if (status != napi_ok) {
301         HILOG_ERROR_I18N("CreateStyleoption: create int32 failed");
302         return nullptr;
303     }
304     napi_value styledKey = nullptr;
305     status = napi_create_int32(env, 0, &styledKey);
306     if (status != napi_ok) {
307         HILOG_ERROR_I18N("CreateStyleoption: create int32 failed");
308         return nullptr;
309     }
310     SetNamedProperty(env, option, "start", start);
311     SetNamedProperty(env, option, "length", length);
312     SetNamedProperty(env, option, "styledKey", styledKey);
313     SetNamedProperty(env, option, "styledValue", textStyle);
314     return option;
315 }
316 
SetNamedProperty(napi_env env,napi_value & option,const std::string & name,napi_value & property)317 void StyledNumberFormatAddon::SetNamedProperty(napi_env env, napi_value &option, const std::string& name,
318     napi_value &property)
319 {
320     napi_status status = napi_set_named_property(env, option, name.c_str(), property);
321     if (status != napi_ok) {
322         HILOG_ERROR_I18N("SetNamedProperty: set property %{public}s failed", name.c_str());
323     }
324 }
325 
GetNumberFmtOrSimpleNumberFmt(napi_env env,napi_value arg,JSNumberFormatAddon ** jsNumberformatAddon,SimpleNumberFormatAddon ** simpleNumberFormatAddon,NumberFormatAddon ** numberFormatAddon)326 void StyledNumberFormatAddon::GetNumberFmtOrSimpleNumberFmt(napi_env env, napi_value arg,
327     JSNumberFormatAddon **jsNumberformatAddon, SimpleNumberFormatAddon **simpleNumberFormatAddon,
328     NumberFormatAddon **numberFormatAddon)
329 {
330     napi_valuetype valueType = napi_valuetype::napi_undefined;
331     napi_status status = napi_typeof(env, arg, &valueType);
332     if (status != napi_ok) {
333         HILOG_ERROR_I18N("GetNumberFmtOrSimpleNumberFmt Failed to get type.");
334         return;
335     } else if (valueType != napi_valuetype::napi_object) {
336         ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "intl.NumberFormat or SimpleNumberFormat", "", true);
337         return;
338     }
339     if (IsIntlNumberFormatInstance(env, arg)) {
340         status = napi_unwrap(env, arg, reinterpret_cast<void **>(jsNumberformatAddon));
341         if (status != napi_ok && jsNumberformatAddon != nullptr) {
342             *jsNumberformatAddon = nullptr;
343         }
344     } else if (IsNumberFormatInstance(env, arg, "@ohos.intl")) {
345         status = napi_unwrap(env, arg, reinterpret_cast<void **>(numberFormatAddon));
346         if (status != napi_ok && numberFormatAddon != nullptr) {
347             *numberFormatAddon = nullptr;
348         }
349     } else {
350         status = napi_unwrap(env, arg, reinterpret_cast<void **>(simpleNumberFormatAddon));
351         if (status != napi_ok && simpleNumberFormatAddon != nullptr) {
352             *simpleNumberFormatAddon = nullptr;
353         }
354     }
355 }
356 
IsNumberFormatInstance(napi_env env,napi_value arg,const std::string & moduleName)357 bool StyledNumberFormatAddon::IsNumberFormatInstance(napi_env env, napi_value arg, const std::string &moduleName)
358 {
359     napi_value numFmtConstructor = nullptr;
360     napi_value intlModule;
361     napi_status status = napi_load_module(env, moduleName.c_str(), &intlModule);
362     if (status != napi_ok) {
363         HILOG_ERROR_I18N("StyledNumberFormatAddon::IsNumberFormatInstance Failed to get %{public}s module.",
364             moduleName.c_str());
365         return false;
366     }
367     status = napi_get_named_property(env, intlModule, "NumberFormat", &numFmtConstructor);
368     if (status != napi_ok) {
369         HILOG_ERROR_I18N("StyledNumberFormatAddon::IsNumberFormatInstance: get NumberFormat  constructor failed");
370         return false;
371     }
372     bool isInstance = true;
373     status = napi_instanceof(env, arg, numFmtConstructor, &isInstance);
374     if (status != napi_ok) {
375         HILOG_ERROR_I18N("StyledNumberFormatAddon::IsNumberFormatInstance Failed to get instance of argv 0.");
376         return false;
377     }
378     return isInstance;
379 }
380 
IsIntlNumberFormatInstance(napi_env env,napi_value arg)381 bool StyledNumberFormatAddon::IsIntlNumberFormatInstance(napi_env env, napi_value arg)
382 {
383     napi_value global = nullptr;
384     napi_status status = napi_get_global(env, &global);
385     if (status != napi_ok || global == nullptr) {
386         HILOG_ERROR_I18N("IsIntlNumberFormatInstance: get global obj failed");
387         return false;
388     }
389     napi_value intlModule;
390     status = napi_get_named_property(env, global, "Intl", &intlModule);
391     if (status != napi_ok) {
392         HILOG_ERROR_I18N("StyledNumberFormatAddon::IsIntlNumberFormatInstance Get Intl module failed");
393         return false;
394     }
395     napi_value numFmtConstructor = nullptr;
396     status = napi_get_named_property(env, intlModule, "NumberFormat", &numFmtConstructor);
397     if (status != napi_ok) {
398         HILOG_ERROR_I18N("StyledNumberFormatAddon::IsIntlNumberFormatInstance: get NumberFormat  constructor failed");
399         return false;
400     }
401     bool isInstance = true;
402     status = napi_instanceof(env, arg, numFmtConstructor, &isInstance);
403     if (status != napi_ok) {
404         HILOG_ERROR_I18N("StyledNumberFormatAddon::IsIntlNumberFormatInstance napi_instanceof failed");
405         return false;
406     }
407     return isInstance;
408 }
409 } // namespace I18n
410 } // namespace Global
411 } // namespace OHOS