1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "number_format_addon.h"
17
18 #include "i18n_hilog.h"
19 #include "utils.h"
20 #include "variable_converter.h"
21
22 using namespace OHOS;
23 using namespace Global;
24 using namespace I18n;
25
UnwrapAddon(ani_env * env,ani_object object,const std::string & name)26 NumberFormatAddon* NumberFormatAddon::UnwrapAddon(ani_env *env, ani_object object, const std::string &name)
27 {
28 ani_long ptr;
29 if (ANI_OK != env->Object_GetFieldByName_Long(object, name.c_str(), &ptr)) {
30 HILOG_ERROR_I18N("Get Long '%{public}s' failed", name.c_str());
31 return nullptr;
32 }
33 return reinterpret_cast<NumberFormatAddon*>(ptr);
34 }
35
NumberFormat_Format(ani_env * env,ani_object object,ani_double number)36 ani_string NumberFormatAddon::NumberFormat_Format(ani_env *env, ani_object object, ani_double number)
37 {
38 NumberFormatAddon *obj = UnwrapAddon(env, object, "nativeNumberFormat");
39 if (obj == nullptr || obj->numberfmt_ == nullptr) {
40 HILOG_ERROR_I18N("FormatNumber: Get NumberFormat object failed");
41 return nullptr;
42 }
43
44 std::string str = obj->numberfmt_->Format(number);
45 return VariableConverter::StringToAniStr(env, str);
46 }
47
NumberFormat_FormatRange(ani_env * env,ani_object object,ani_double startRange,ani_double endRange)48 ani_string NumberFormatAddon::NumberFormat_FormatRange(ani_env *env, ani_object object,
49 ani_double startRange, ani_double endRange)
50 {
51 NumberFormatAddon *obj = UnwrapAddon(env, object, "nativeNumberFormat");
52 if (obj == nullptr || obj->numberfmt_ == nullptr) {
53 HILOG_ERROR_I18N("FormatRangeNumber: Get NumberFormat object failed");
54 return nullptr;
55 }
56
57 std::string str = obj->numberfmt_->FormatRange(startRange, endRange);
58 return VariableConverter::StringToAniStr(env, str);
59 }
60
NumberFormat_ResolvedOptions(ani_env * env,ani_object object)61 ani_object NumberFormatAddon::NumberFormat_ResolvedOptions(ani_env *env, ani_object object)
62 {
63 NumberFormatAddon *obj = UnwrapAddon(env, object, "nativeNumberFormat");
64 if (obj == nullptr || obj->numberfmt_ == nullptr) {
65 HILOG_ERROR_I18N("GetNumberResolvedOptions: Get NumberFormat object failed");
66 return nullptr;
67 }
68 std::map<std::string, std::string> options = {};
69 obj->numberfmt_->GetResolvedOptions(options);
70
71 static const char *className = "L@ohos/intl/intl/NumberOptionsInner;";
72 ani_class cls;
73 if (ANI_OK != env->FindClass(className, &cls)) {
74 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
75 return nullptr;
76 }
77
78 ani_method ctor;
79 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
80 HILOG_ERROR_I18N("Find method '<ctor>' failed");
81 return nullptr;
82 }
83
84 ani_object numberOptions;
85 if (ANI_OK != env->Object_New(cls, ctor, &numberOptions)) {
86 HILOG_ERROR_I18N("New object '%{public}s' failed", className);
87 return nullptr;
88 }
89
90 std::vector<std::string> keys = {
91 "locale", "currency", "currencySign", "currencyDisplay", "unit", "unitDisplay", "unitUsage",
92 "signDisplay", "compactDisplay", "notation", "localeMatcher", "style", "numberingSystem",
93 "roundingPriority", "roundingMode", "minimumIntegerDigits", "minimumFractionDigits", "maximumFractionDigits",
94 "minimumSignificantDigits", "maximumSignificantDigits", "roundingIncrement"
95 };
96
97 for (const auto& key : keys) {
98 if (options.find(key) != options.end()) {
99 if (key == "minimumIntegerDigits" || key == "minimumFractionDigits" ||
100 key == "maximumFractionDigits" || key == "minimumSignificantDigits" ||
101 key == "maximumSignificantDigits" || key == "roundingIncrement") {
102 VariableConverter::SetNumberMember(env, numberOptions, key, options[key]);
103 } else {
104 VariableConverter::SetStringMember(env, numberOptions, key, options[key]);
105 }
106 }
107 }
108 VariableConverter::SetBooleanMember(env, numberOptions, "useGrouping", options["useGrouping"]);
109 return numberOptions;
110 }
111
NumberFormat_Create(ani_env * env,ani_object object)112 ani_object NumberFormatAddon::NumberFormat_Create(ani_env *env, [[maybe_unused]] ani_object object)
113 {
114 std::vector<std::string> localeTags;
115 std::map<std::string, std::string> map = {};
116 std::unique_ptr<NumberFormatAddon> obj = std::make_unique<NumberFormatAddon>();
117 obj->numberfmt_ = std::make_unique<NumberFormat>(localeTags, map);
118
119 static const char *className = "L@ohos/intl/intl/NumberFormat;";
120 ani_object numberFormatObject = VariableConverter::CreateAniObject(env, className, obj.get());
121 obj.release();
122 return numberFormatObject;
123 }
124
GetNumberFormatOptions(ani_env * env,ani_object options)125 static std::map<std::string, std::string> GetNumberFormatOptions(ani_env *env, ani_object options)
126 {
127 std::map<std::string, std::string> map;
128 ani_boolean isUndefined;
129 if (ANI_OK != env->Reference_IsUndefined(options, &isUndefined)) {
130 HILOG_ERROR_I18N("Reference IsUndefined failed");
131 return map;
132 }
133 if (!isUndefined) {
134 std::vector<std::string> stringKeys = {
135 "locale", "currency", "currencySign", "currencyDisplay", "unit", "unitDisplay", "unitUsage",
136 "signDisplay", "compactDisplay", "notation", "localeMatcher", "style", "numberingSystem"
137 };
138 for (const auto& key : stringKeys) {
139 std::string value;
140 if (VariableConverter::GetStringMember(env, options, key, value)) {
141 map.insert(std::make_pair(key, value));
142 }
143 }
144
145 std::vector<std::pair<std::string, int>> intKeys = {
146 {"minimumIntegerDigits", 0}, {"minimumFractionDigits", 0}, {"maximumFractionDigits", 0},
147 {"minimumSignificantDigits", 0}, {"maximumSignificantDigits", 0}, {"roundingIncrement", 0}
148 };
149 for (auto& [key, value] : intKeys) {
150 if (VariableConverter::GetNumberMember(env, options, key, value)) {
151 map.insert(std::make_pair(key, std::to_string(value)));
152 }
153 }
154
155 bool useGrouping;
156 if (VariableConverter::GetBooleanMember(env, options, "useGrouping", useGrouping)) {
157 map.insert(std::make_pair("useGrouping", useGrouping ? "true" : "false"));
158 }
159 }
160 return map;
161 }
162
NumberFormat_CreateWithParam(ani_env * env,ani_object object,ani_object locale,ani_object options)163 ani_object NumberFormatAddon::NumberFormat_CreateWithParam(ani_env *env, [[maybe_unused]] ani_object object,
164 ani_object locale, ani_object options)
165 {
166 std::vector<std::string> localeTags = VariableConverter::GetLocaleTags(env, locale);
167 std::map<std::string, std::string> map = GetNumberFormatOptions(env, options);
168
169 std::unique_ptr<NumberFormatAddon> obj = std::make_unique<NumberFormatAddon>();
170 obj->numberfmt_ = std::make_unique<NumberFormat>(localeTags, map);
171
172 static const char *className = "L@ohos/intl/intl/NumberFormat;";
173 ani_object numberFormatObject = VariableConverter::CreateAniObject(env, className, obj.get());
174 obj.release();
175 return numberFormatObject;
176 }
177
BindContext_NumberFormat(ani_env * env)178 ani_status NumberFormatAddon::BindContext_NumberFormat(ani_env *env)
179 {
180 static const char *className = "L@ohos/intl/intl/NumberFormat;";
181 ani_class cls;
182 if (ANI_OK != env->FindClass(className, &cls)) {
183 HILOG_ERROR_I18N("Find class '%{public}s' failed", className);
184 return ANI_ERROR;
185 }
186
187 std::array methods = {
188 ani_native_function {"create", ":L@ohos/intl/intl/NumberFormat;",
189 reinterpret_cast<void *>(NumberFormat_Create) },
190 ani_native_function {"create",
191 "Lstd/core/Object;L@ohos/intl/intl/NumberOptions;:L@ohos/intl/intl/NumberFormat;",
192 reinterpret_cast<void *>(NumberFormat_CreateWithParam) },
193 ani_native_function {"format", nullptr, reinterpret_cast<void *>(NumberFormat_Format) },
194 ani_native_function {"formatRange", nullptr, reinterpret_cast<void *>(NumberFormat_FormatRange) },
195 ani_native_function {"resolvedOptions", nullptr, reinterpret_cast<void *>(NumberFormat_ResolvedOptions) },
196 };
197
198 if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
199 HILOG_ERROR_I18N("Cannot bind native methods to '%{public}s'", className);
200 return ANI_ERROR;
201 };
202 return ANI_OK;
203 }
204