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
25U_NAMESPACE_USE
26
27
28class BNumberFormatImpl {
29public:
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
43private:
44	NumberFormat*	fIntegerFormat;
45	NumberFormat*	fFloatFormat;
46	NumberFormat*	fCurrencyFormat;
47	NumberFormat*	fPercentFormat;
48};
49
50
51BNumberFormatImpl::BNumberFormatImpl()
52{
53	// They are initialized lazily as needed
54	fIntegerFormat = NULL;
55	fFloatFormat = NULL;
56	fCurrencyFormat = NULL;
57	fPercentFormat = NULL;
58}
59
60
61BNumberFormatImpl::~BNumberFormatImpl()
62{
63	delete fIntegerFormat;
64	delete fFloatFormat;
65	delete fCurrencyFormat;
66	delete fPercentFormat;
67}
68
69
70NumberFormat*
71BNumberFormatImpl::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
92NumberFormat*
93BNumberFormatImpl::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
114NumberFormat*
115BNumberFormatImpl::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
136NumberFormat*
137BNumberFormatImpl::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
158ssize_t
159BNumberFormatImpl::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
171status_t
172BNumberFormatImpl::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
189BNumberFormat::BNumberFormat()
190	: BFormat()
191{
192	fPrivateData = new BNumberFormatImpl();
193}
194
195
196BNumberFormat::BNumberFormat(const BLocale* locale)
197	: BFormat(locale)
198{
199	fPrivateData = new BNumberFormatImpl();
200}
201
202
203BNumberFormat::BNumberFormat(const BNumberFormat &other)
204	: BFormat(other)
205{
206	fPrivateData = new BNumberFormatImpl(*other.fPrivateData);
207}
208
209
210BNumberFormat::~BNumberFormat()
211{
212	delete fPrivateData;
213}
214
215
216// #pragma mark - Formatting
217
218
219ssize_t
220BNumberFormat::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
231status_t
232BNumberFormat::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
250ssize_t
251BNumberFormat::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
262status_t
263BNumberFormat::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
281status_t
282BNumberFormat::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
304ssize_t
305BNumberFormat::FormatMonetary(char* string, size_t maxSize, const double value)
306{
307	return fPrivateData->ApplyFormatter(
308		fPrivateData->GetCurrency(&fConventions), string, maxSize, value);
309}
310
311
312status_t
313BNumberFormat::FormatMonetary(BString& string, const double value)
314{
315	return fPrivateData->ApplyFormatter(
316		fPrivateData->GetCurrency(&fConventions), string, value);
317}
318
319
320ssize_t
321BNumberFormat::FormatPercent(char* string, size_t maxSize, const double value)
322{
323	return fPrivateData->ApplyFormatter(
324		fPrivateData->GetPercent(&fConventions), string, maxSize, value);
325}
326
327
328status_t
329BNumberFormat::FormatPercent(BString& string, const double value)
330{
331	return fPrivateData->ApplyFormatter(
332		fPrivateData->GetPercent(&fConventions), string, value);
333}
334
335
336status_t
337BNumberFormat::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
359BString
360BNumberFormat::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