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