xref: /haiku/src/kits/locale/NumberFormat.cpp (revision 9aed476999435ab236c8da10e6b8815833cd5a63)
1 /*
2  * Copyright 2003, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
4  * Copyright 2012, John Scipione, jscipione@gmail.com
5  * Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
6  * Copyright 2021, Andrew Lindesay, apl@lindesay.co.nz
7  * All rights reserved. Distributed under the terms of the MIT License.
8  */
9 
10 
11 #include <unicode/uversion.h>
12 #include <NumberFormat.h>
13 
14 #include <AutoDeleter.h>
15 #include <Autolock.h>
16 #include <FormattingConventionsPrivate.h>
17 
18 #include <ICUWrapper.h>
19 
20 #include <unicode/dcfmtsym.h>
21 #include <unicode/decimfmt.h>
22 #include <unicode/numfmt.h>
23 
24 
25 U_NAMESPACE_USE
26 
27 
28 class BNumberFormatImpl {
29 public:
30 					BNumberFormatImpl();
31 					~BNumberFormatImpl();
32 
33 	NumberFormat*	GetInteger(BFormattingConventions* convention);
34 	NumberFormat*	GetFloat(BFormattingConventions* convention);
35 	NumberFormat*	GetCurrency(BFormattingConventions* convention);
36 	NumberFormat*	GetPercent(BFormattingConventions* convention);
37 
38 	ssize_t			ApplyFormatter(NumberFormat* formatter, char* string,
39 						size_t maxSize, const double value);
40 	status_t		ApplyFormatter(NumberFormat* formatter, BString& string,
41 						const double value);
42 
43 private:
44 	NumberFormat*	fIntegerFormat;
45 	NumberFormat*	fFloatFormat;
46 	NumberFormat*	fCurrencyFormat;
47 	NumberFormat*	fPercentFormat;
48 };
49 
50 
51 BNumberFormatImpl::BNumberFormatImpl()
52 {
53 	// They are initialized lazily as needed
54 	fIntegerFormat = NULL;
55 	fFloatFormat = NULL;
56 	fCurrencyFormat = NULL;
57 	fPercentFormat = NULL;
58 }
59 
60 
61 BNumberFormatImpl::~BNumberFormatImpl()
62 {
63 	delete fIntegerFormat;
64 	delete fFloatFormat;
65 	delete fCurrencyFormat;
66 	delete fPercentFormat;
67 }
68 
69 
70 NumberFormat*
71 BNumberFormatImpl::GetInteger(BFormattingConventions* convention)
72 {
73 	if (fIntegerFormat == NULL) {
74 		UErrorCode err = U_ZERO_ERROR;
75 		fIntegerFormat = NumberFormat::createInstance(
76 			*BFormattingConventions::Private(convention).ICULocale(),
77 			UNUM_DECIMAL, err);
78 
79 		if (fIntegerFormat == NULL)
80 			return NULL;
81 		if (U_FAILURE(err)) {
82 			delete fIntegerFormat;
83 			fIntegerFormat = NULL;
84 			return NULL;
85 		}
86 	}
87 
88 	return fIntegerFormat;
89 }
90 
91 
92 NumberFormat*
93 BNumberFormatImpl::GetFloat(BFormattingConventions* convention)
94 {
95 	if (fFloatFormat == NULL) {
96 		UErrorCode err = U_ZERO_ERROR;
97 		fFloatFormat = NumberFormat::createInstance(
98 			*BFormattingConventions::Private(convention).ICULocale(),
99 			UNUM_DECIMAL, err);
100 
101 		if (fFloatFormat == NULL)
102 			return NULL;
103 		if (U_FAILURE(err)) {
104 			delete fFloatFormat;
105 			fFloatFormat = NULL;
106 			return NULL;
107 		}
108 	}
109 
110 	return fFloatFormat;
111 }
112 
113 
114 NumberFormat*
115 BNumberFormatImpl::GetCurrency(BFormattingConventions* convention)
116 {
117 	if (fCurrencyFormat == NULL) {
118 		UErrorCode err = U_ZERO_ERROR;
119 		fCurrencyFormat = NumberFormat::createCurrencyInstance(
120 			*BFormattingConventions::Private(convention).ICULocale(),
121 			err);
122 
123 		if (fCurrencyFormat == NULL)
124 			return NULL;
125 		if (U_FAILURE(err)) {
126 			delete fCurrencyFormat;
127 			fCurrencyFormat = NULL;
128 			return NULL;
129 		}
130 	}
131 
132 	return fCurrencyFormat;
133 }
134 
135 
136 NumberFormat*
137 BNumberFormatImpl::GetPercent(BFormattingConventions* convention)
138 {
139 	if (fPercentFormat == NULL) {
140 		UErrorCode err = U_ZERO_ERROR;
141 		fPercentFormat = NumberFormat::createInstance(
142 			*BFormattingConventions::Private(convention).ICULocale(),
143 			UNUM_PERCENT, err);
144 
145 		if (fPercentFormat == NULL)
146 			return NULL;
147 		if (U_FAILURE(err)) {
148 			delete fPercentFormat;
149 			fPercentFormat = NULL;
150 			return NULL;
151 		}
152 	}
153 
154 	return fPercentFormat;
155 }
156 
157 
158 ssize_t
159 BNumberFormatImpl::ApplyFormatter(NumberFormat* formatter, char* string,
160 	size_t maxSize, const double value)
161 {
162 	BString fullString;
163 	status_t status = ApplyFormatter(formatter, fullString, value);
164 	if (status != B_OK)
165 		return status;
166 
167 	return strlcpy(string, fullString.String(), maxSize);
168 }
169 
170 
171 status_t
172 BNumberFormatImpl::ApplyFormatter(NumberFormat* formatter, BString& string,
173 	const double value)
174 {
175 	if (formatter == NULL)
176 		return B_NO_MEMORY;
177 
178 	UnicodeString icuString;
179 	formatter->format(value, icuString);
180 
181 	string.Truncate(0);
182 	BStringByteSink stringConverter(&string);
183 	icuString.toUTF8(stringConverter);
184 
185 	return B_OK;
186 }
187 
188 
189 BNumberFormat::BNumberFormat(const BLocale* locale)
190 	: BFormat(locale)
191 {
192 	fPrivateData = new BNumberFormatImpl();
193 }
194 
195 
196 BNumberFormat::BNumberFormat(const BNumberFormat &other)
197 	: BFormat(other)
198 {
199 	fPrivateData = new BNumberFormatImpl(*other.fPrivateData);
200 }
201 
202 
203 BNumberFormat::~BNumberFormat()
204 {
205 	delete fPrivateData;
206 }
207 
208 
209 // #pragma mark - Formatting
210 
211 
212 ssize_t
213 BNumberFormat::Format(char* string, size_t maxSize, const double value)
214 {
215 	BString fullString;
216 	status_t status = Format(fullString, value);
217 	if (status != B_OK)
218 		return status;
219 
220 	return strlcpy(string, fullString.String(), maxSize);
221 }
222 
223 
224 status_t
225 BNumberFormat::Format(BString& string, const double value)
226 {
227 	NumberFormat* formatter = fPrivateData->GetFloat(&fConventions);
228 
229 	if (formatter == NULL)
230 		return B_NO_MEMORY;
231 
232 	UnicodeString icuString;
233 	formatter->format(value, icuString);
234 
235 	string.Truncate(0);
236 	BStringByteSink stringConverter(&string);
237 	icuString.toUTF8(stringConverter);
238 
239 	return B_OK;
240 }
241 
242 
243 ssize_t
244 BNumberFormat::Format(char* string, size_t maxSize, const int32 value)
245 {
246 	BString fullString;
247 	status_t status = Format(fullString, value);
248 	if (status != B_OK)
249 		return status;
250 
251 	return strlcpy(string, fullString.String(), maxSize);
252 }
253 
254 
255 status_t
256 BNumberFormat::Format(BString& string, const int32 value)
257 {
258 	NumberFormat* formatter = fPrivateData->GetInteger(&fConventions);
259 
260 	if (formatter == NULL)
261 		return B_NO_MEMORY;
262 
263 	UnicodeString icuString;
264 	formatter->format((int32_t)value, icuString);
265 
266 	string.Truncate(0);
267 	BStringByteSink stringConverter(&string);
268 	icuString.toUTF8(stringConverter);
269 
270 	return B_OK;
271 }
272 
273 
274 ssize_t
275 BNumberFormat::FormatMonetary(char* string, size_t maxSize, const double value)
276 {
277 	return fPrivateData->ApplyFormatter(
278 		fPrivateData->GetCurrency(&fConventions), string, maxSize, value);
279 }
280 
281 
282 status_t
283 BNumberFormat::FormatMonetary(BString& string, const double value)
284 {
285 	return fPrivateData->ApplyFormatter(
286 		fPrivateData->GetCurrency(&fConventions), string, value);
287 }
288 
289 
290 ssize_t
291 BNumberFormat::FormatPercent(char* string, size_t maxSize, const double value)
292 {
293 	return fPrivateData->ApplyFormatter(
294 		fPrivateData->GetPercent(&fConventions), string, maxSize, value);
295 }
296 
297 
298 status_t
299 BNumberFormat::FormatPercent(BString& string, const double value)
300 {
301 	return fPrivateData->ApplyFormatter(
302 		fPrivateData->GetPercent(&fConventions), string, value);
303 }
304 
305 
306 status_t
307 BNumberFormat::Parse(const BString& string, double& value)
308 {
309 	NumberFormat* parser = fPrivateData->GetFloat(&fConventions);
310 
311 	if (parser == NULL)
312 		return B_NO_MEMORY;
313 
314 	UnicodeString unicode(string.String());
315 	Formattable result(0);
316 	UErrorCode err = U_ZERO_ERROR;
317 
318 	parser->parse(unicode, result, err);
319 
320 	if (err != U_ZERO_ERROR)
321 		return B_BAD_DATA;
322 
323 	value = result.getDouble();
324 
325 	return B_OK;
326 }
327 
328 
329 BString
330 BNumberFormat::GetSeparator(BNumberElement element)
331 {
332 	DecimalFormatSymbols::ENumberFormatSymbol symbol;
333 	BString result;
334 
335 	switch(element) {
336 		case B_DECIMAL_SEPARATOR:
337 			symbol = DecimalFormatSymbols::kDecimalSeparatorSymbol;
338 			break;
339 		case B_GROUPING_SEPARATOR:
340 			symbol = DecimalFormatSymbols::kGroupingSeparatorSymbol;
341 			break;
342 
343 		default:
344 			return result;
345 	}
346 
347 	NumberFormat* format = fPrivateData->GetFloat(&fConventions);
348 	DecimalFormat* decimal = dynamic_cast<DecimalFormat*>(format);
349 
350 	if (decimal == NULL)
351 		return result;
352 
353 	const DecimalFormatSymbols* symbols = decimal->getDecimalFormatSymbols();
354 	UnicodeString string = symbols->getSymbol(symbol);
355 
356 	BStringByteSink stringConverter(&result);
357 	string.toUTF8(stringConverter);
358 
359 	return result;
360 }
361