xref: /haiku/src/kits/locale/NumberFormat.cpp (revision 3761b2f4207ccac01d028608b724f807ca142427)
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()
190 	: BFormat()
191 {
192 	fPrivateData = new BNumberFormatImpl();
193 }
194 
195 
196 BNumberFormat::BNumberFormat(const BLocale* locale)
197 	: BFormat(locale)
198 {
199 	fPrivateData = new BNumberFormatImpl();
200 }
201 
202 
203 BNumberFormat::BNumberFormat(const BNumberFormat &other)
204 	: BFormat(other)
205 {
206 	fPrivateData = new BNumberFormatImpl(*other.fPrivateData);
207 }
208 
209 
210 BNumberFormat::~BNumberFormat()
211 {
212 	delete fPrivateData;
213 }
214 
215 
216 // #pragma mark - Formatting
217 
218 
219 ssize_t
220 BNumberFormat::Format(char* string, size_t maxSize, const double value)
221 {
222 	BString fullString;
223 	status_t status = Format(fullString, value);
224 	if (status != B_OK)
225 		return status;
226 
227 	return strlcpy(string, fullString.String(), maxSize);
228 }
229 
230 
231 status_t
232 BNumberFormat::Format(BString& string, const double value)
233 {
234 	NumberFormat* formatter = fPrivateData->GetFloat(&fConventions);
235 
236 	if (formatter == NULL)
237 		return B_NO_MEMORY;
238 
239 	UnicodeString icuString;
240 	formatter->format(value, icuString);
241 
242 	string.Truncate(0);
243 	BStringByteSink stringConverter(&string);
244 	icuString.toUTF8(stringConverter);
245 
246 	return B_OK;
247 }
248 
249 
250 ssize_t
251 BNumberFormat::Format(char* string, size_t maxSize, const int32 value)
252 {
253 	BString fullString;
254 	status_t status = Format(fullString, value);
255 	if (status != B_OK)
256 		return status;
257 
258 	return strlcpy(string, fullString.String(), maxSize);
259 }
260 
261 
262 status_t
263 BNumberFormat::Format(BString& string, const int32 value)
264 {
265 	NumberFormat* formatter = fPrivateData->GetInteger(&fConventions);
266 
267 	if (formatter == NULL)
268 		return B_NO_MEMORY;
269 
270 	UnicodeString icuString;
271 	formatter->format((int32_t)value, icuString);
272 
273 	string.Truncate(0);
274 	BStringByteSink stringConverter(&string);
275 	icuString.toUTF8(stringConverter);
276 
277 	return B_OK;
278 }
279 
280 
281 status_t
282 BNumberFormat::SetPrecision(int precision)
283 {
284 	NumberFormat* decimalFormatter = fPrivateData->GetFloat(&fConventions);
285 	NumberFormat* currencyFormatter = fPrivateData->GetCurrency(&fConventions);
286 	NumberFormat* percentFormatter = fPrivateData->GetPercent(&fConventions);
287 
288 	if ((decimalFormatter == NULL) || (currencyFormatter == NULL) || (percentFormatter == NULL))
289 		return B_ERROR;
290 
291 	decimalFormatter->setMinimumFractionDigits(precision);
292 	decimalFormatter->setMaximumFractionDigits(precision);
293 
294 	currencyFormatter->setMinimumFractionDigits(precision);
295 	currencyFormatter->setMaximumFractionDigits(precision);
296 
297 	percentFormatter->setMinimumFractionDigits(precision);
298 	percentFormatter->setMaximumFractionDigits(precision);
299 
300 	return B_OK;
301 }
302 
303 
304 ssize_t
305 BNumberFormat::FormatMonetary(char* string, size_t maxSize, const double value)
306 {
307 	return fPrivateData->ApplyFormatter(
308 		fPrivateData->GetCurrency(&fConventions), string, maxSize, value);
309 }
310 
311 
312 status_t
313 BNumberFormat::FormatMonetary(BString& string, const double value)
314 {
315 	return fPrivateData->ApplyFormatter(
316 		fPrivateData->GetCurrency(&fConventions), string, value);
317 }
318 
319 
320 ssize_t
321 BNumberFormat::FormatPercent(char* string, size_t maxSize, const double value)
322 {
323 	return fPrivateData->ApplyFormatter(
324 		fPrivateData->GetPercent(&fConventions), string, maxSize, value);
325 }
326 
327 
328 status_t
329 BNumberFormat::FormatPercent(BString& string, const double value)
330 {
331 	return fPrivateData->ApplyFormatter(
332 		fPrivateData->GetPercent(&fConventions), string, value);
333 }
334 
335 
336 status_t
337 BNumberFormat::Parse(const BString& string, double& value)
338 {
339 	NumberFormat* parser = fPrivateData->GetFloat(&fConventions);
340 
341 	if (parser == NULL)
342 		return B_NO_MEMORY;
343 
344 	UnicodeString unicode(string.String());
345 	Formattable result(0);
346 	UErrorCode err = U_ZERO_ERROR;
347 
348 	parser->parse(unicode, result, err);
349 
350 	if (err != U_ZERO_ERROR)
351 		return B_BAD_DATA;
352 
353 	value = result.getDouble();
354 
355 	return B_OK;
356 }
357 
358 
359 BString
360 BNumberFormat::GetSeparator(BNumberElement element)
361 {
362 	DecimalFormatSymbols::ENumberFormatSymbol symbol;
363 	BString result;
364 
365 	switch(element) {
366 		case B_DECIMAL_SEPARATOR:
367 			symbol = DecimalFormatSymbols::kDecimalSeparatorSymbol;
368 			break;
369 		case B_GROUPING_SEPARATOR:
370 			symbol = DecimalFormatSymbols::kGroupingSeparatorSymbol;
371 			break;
372 
373 		default:
374 			return result;
375 	}
376 
377 	NumberFormat* format = fPrivateData->GetFloat(&fConventions);
378 	DecimalFormat* decimal = dynamic_cast<DecimalFormat*>(format);
379 
380 	if (decimal == NULL)
381 		return result;
382 
383 	const DecimalFormatSymbols* symbols = decimal->getDecimalFormatSymbols();
384 	UnicodeString string = symbols->getSymbol(symbol);
385 
386 	BStringByteSink stringConverter(&result);
387 	string.toUTF8(stringConverter);
388 
389 	return result;
390 }
391