1 /*
2 * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "ICUMonetaryData.h"
8
9 #include <langinfo.h>
10 #include <limits.h>
11 #include <string.h>
12 #include <strings.h>
13
14
15 U_NAMESPACE_USE
16
17
18 namespace BPrivate {
19 namespace Libroot {
20
21
ICUMonetaryData(pthread_key_t tlsKey,struct lconv & localeConv)22 ICUMonetaryData::ICUMonetaryData(pthread_key_t tlsKey, struct lconv& localeConv)
23 :
24 inherited(tlsKey),
25 fLocaleConv(localeConv),
26 fPosixLocaleConv(NULL)
27 {
28 fLocaleConv.int_curr_symbol = fIntCurrSymbol;
29 fLocaleConv.currency_symbol = fCurrencySymbol;
30 fLocaleConv.mon_decimal_point = fDecimalPoint;
31 fLocaleConv.mon_thousands_sep = fThousandsSep;
32 fLocaleConv.mon_grouping = fGrouping;
33 fLocaleConv.positive_sign = fPositiveSign;
34 fLocaleConv.negative_sign = fNegativeSign;
35 }
36
37
38 void
Initialize(LocaleMonetaryDataBridge * dataBridge)39 ICUMonetaryData::Initialize(LocaleMonetaryDataBridge* dataBridge)
40 {
41 fPosixLocaleConv = dataBridge->posixLocaleConv;
42 }
43
44
45 status_t
SetTo(const Locale & locale,const char * posixLocaleName)46 ICUMonetaryData::SetTo(const Locale& locale, const char* posixLocaleName)
47 {
48 status_t result = inherited::SetTo(locale, posixLocaleName);
49
50 if (result == B_OK) {
51 UErrorCode icuStatus = U_ZERO_ERROR;
52 UChar intlCurrencySeparatorChar = CHAR_MAX;
53 DecimalFormat* currencyFormat = dynamic_cast<DecimalFormat*>(
54 NumberFormat::createCurrencyInstance(locale, icuStatus));
55 if (!U_SUCCESS(icuStatus))
56 return B_UNSUPPORTED;
57 if (!currencyFormat)
58 return B_BAD_TYPE;
59
60 const DecimalFormatSymbols* formatSymbols
61 = currencyFormat->getDecimalFormatSymbols();
62 if (!formatSymbols)
63 result = B_BAD_DATA;
64
65 if (result == B_OK) {
66 result = _SetLocaleconvEntry(formatSymbols, fDecimalPoint,
67 DecimalFormatSymbols::kMonetarySeparatorSymbol);
68 }
69 if (result == B_OK) {
70 result = _SetLocaleconvEntry(formatSymbols, fThousandsSep,
71 DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol);
72 }
73 if (result == B_OK) {
74 int32 groupingSize = currencyFormat->getGroupingSize();
75 if (groupingSize < 1)
76 fGrouping[0] = '\0';
77 else {
78 fGrouping[0] = groupingSize;
79 int32 secondaryGroupingSize
80 = currencyFormat->getSecondaryGroupingSize();
81 if (secondaryGroupingSize < 1)
82 fGrouping[1] = '\0';
83 else {
84 fGrouping[1] = secondaryGroupingSize;
85 fGrouping[2] = '\0';
86 }
87 }
88 }
89 if (result == B_OK) {
90 fLocaleConv.int_frac_digits
91 = currencyFormat->getMinimumFractionDigits();
92 fLocaleConv.frac_digits
93 = currencyFormat->getMinimumFractionDigits();
94 }
95 if (result == B_OK) {
96 UnicodeString positivePrefix, positiveSuffix, negativePrefix,
97 negativeSuffix;
98 currencyFormat->getPositivePrefix(positivePrefix);
99 currencyFormat->getPositiveSuffix(positiveSuffix);
100 currencyFormat->getNegativePrefix(negativePrefix);
101 currencyFormat->getNegativeSuffix(negativeSuffix);
102 UnicodeString currencySymbol = formatSymbols->getSymbol(
103 DecimalFormatSymbols::kCurrencySymbol);
104 UnicodeString plusSymbol = formatSymbols->getSymbol(
105 DecimalFormatSymbols::kPlusSignSymbol);
106 UnicodeString minusSymbol = formatSymbols->getSymbol(
107 DecimalFormatSymbols::kMinusSignSymbol);
108
109 // fill national values
110 int32 positiveCurrencyFlags = _DetermineCurrencyPosAndSeparator(
111 positivePrefix, positiveSuffix, plusSymbol, currencySymbol,
112 intlCurrencySeparatorChar);
113 fLocaleConv.p_cs_precedes
114 = (positiveCurrencyFlags & kCsPrecedesFlag) ? 1 : 0;
115 fLocaleConv.p_sep_by_space
116 = (positiveCurrencyFlags & kSepBySpaceFlag) ? 1 : 0;
117
118 int32 negativeCurrencyFlags = _DetermineCurrencyPosAndSeparator(
119 negativePrefix, negativeSuffix, minusSymbol, currencySymbol,
120 intlCurrencySeparatorChar);
121 fLocaleConv.n_cs_precedes
122 = (negativeCurrencyFlags & kCsPrecedesFlag) ? 1 : 0;
123 fLocaleConv.n_sep_by_space
124 = (negativeCurrencyFlags & kSepBySpaceFlag) ? 1 : 0;
125
126 fLocaleConv.p_sign_posn = _DetermineSignPos(positivePrefix,
127 positiveSuffix, plusSymbol, currencySymbol);
128 fLocaleConv.n_sign_posn = _DetermineSignPos(negativePrefix,
129 negativeSuffix, minusSymbol, currencySymbol);
130 if (fLocaleConv.p_sign_posn == CHAR_MAX) {
131 // usually there is no positive sign indicator, so we
132 // adopt the sign pos of the negative sign symbol
133 fLocaleConv.p_sign_posn = fLocaleConv.n_sign_posn;
134 }
135
136 // copy national to international values, as ICU does not seem
137 // to have separate info for those
138 fLocaleConv.int_p_cs_precedes = fLocaleConv.p_cs_precedes;
139 fLocaleConv.int_p_sep_by_space = fLocaleConv.p_sep_by_space;
140 fLocaleConv.int_n_cs_precedes = fLocaleConv.n_cs_precedes;
141 fLocaleConv.int_n_sep_by_space = fLocaleConv.n_sep_by_space;
142 fLocaleConv.int_p_sign_posn = fLocaleConv.p_sign_posn;
143 fLocaleConv.int_n_sign_posn = fLocaleConv.n_sign_posn;
144
145 // only set sign symbols if they are actually used in any pattern
146 if (positivePrefix.indexOf(plusSymbol) > -1
147 || positiveSuffix.indexOf(plusSymbol) > -1) {
148 result = _SetLocaleconvEntry(formatSymbols, fPositiveSign,
149 DecimalFormatSymbols::kPlusSignSymbol);
150 } else
151 fPositiveSign[0] = '\0';
152 if (negativePrefix.indexOf(minusSymbol) > -1
153 || negativeSuffix.indexOf(minusSymbol) > -1) {
154 result = _SetLocaleconvEntry(formatSymbols, fNegativeSign,
155 DecimalFormatSymbols::kMinusSignSymbol);
156 } else
157 fNegativeSign[0] = '\0';
158 }
159 if (result == B_OK) {
160 UnicodeString intlCurrencySymbol = formatSymbols->getSymbol(
161 DecimalFormatSymbols::kIntlCurrencySymbol);
162 if (intlCurrencySeparatorChar != CHAR_MAX)
163 intlCurrencySymbol += intlCurrencySeparatorChar;
164 else
165 intlCurrencySymbol += ' ';
166 result = _ConvertUnicodeStringToLocaleconvEntry(intlCurrencySymbol,
167 fIntCurrSymbol, skLCBufSize);
168 }
169 if (result == B_OK) {
170 result = _SetLocaleconvEntry(formatSymbols, fCurrencySymbol,
171 DecimalFormatSymbols::kCurrencySymbol);
172 if (fCurrencySymbol[0] == '\0') {
173 // fall back to the international currency symbol
174 result = _SetLocaleconvEntry(formatSymbols, fCurrencySymbol,
175 DecimalFormatSymbols::kIntlCurrencySymbol);
176 fCurrencySymbol[3] = '\0';
177 // drop separator char that's contained in int-curr-symbol
178 }
179 }
180
181 delete currencyFormat;
182 }
183
184 return result;
185 }
186
187
188 status_t
SetToPosix()189 ICUMonetaryData::SetToPosix()
190 {
191 status_t result = inherited::SetToPosix();
192
193 if (result == B_OK) {
194 strcpy(fDecimalPoint, fPosixLocaleConv->mon_decimal_point);
195 strcpy(fThousandsSep, fPosixLocaleConv->mon_thousands_sep);
196 strcpy(fGrouping, fPosixLocaleConv->mon_grouping);
197 strcpy(fCurrencySymbol, fPosixLocaleConv->currency_symbol);
198 strcpy(fIntCurrSymbol, fPosixLocaleConv->int_curr_symbol);
199 strcpy(fPositiveSign, fPosixLocaleConv->positive_sign);
200 strcpy(fNegativeSign, fPosixLocaleConv->negative_sign);
201 fLocaleConv.int_frac_digits = fPosixLocaleConv->int_frac_digits;
202 fLocaleConv.frac_digits = fPosixLocaleConv->frac_digits;
203 fLocaleConv.p_cs_precedes = fPosixLocaleConv->p_cs_precedes;
204 fLocaleConv.p_sep_by_space = fPosixLocaleConv->p_sep_by_space;
205 fLocaleConv.n_cs_precedes = fPosixLocaleConv->n_cs_precedes;
206 fLocaleConv.n_sep_by_space = fPosixLocaleConv->n_sep_by_space;
207 fLocaleConv.p_sign_posn = fPosixLocaleConv->p_sign_posn;
208 fLocaleConv.n_sign_posn = fPosixLocaleConv->n_sign_posn;
209 fLocaleConv.int_p_cs_precedes = fPosixLocaleConv->int_p_cs_precedes;
210 fLocaleConv.int_p_sep_by_space = fPosixLocaleConv->int_p_sep_by_space;
211 fLocaleConv.int_n_cs_precedes = fPosixLocaleConv->int_n_cs_precedes;
212 fLocaleConv.int_n_sep_by_space = fPosixLocaleConv->int_n_sep_by_space;
213 fLocaleConv.int_p_sign_posn = fPosixLocaleConv->int_p_sign_posn;
214 fLocaleConv.int_n_sign_posn = fPosixLocaleConv->int_n_sign_posn;
215 }
216
217 return result;
218 }
219
220
221 const char*
GetLanginfo(int index)222 ICUMonetaryData::GetLanginfo(int index)
223 {
224 switch(index) {
225 case CRNCYSTR:
226 return fCurrencySymbol;
227 default:
228 return "";
229 }
230 }
231
232
233 int32
_DetermineCurrencyPosAndSeparator(const UnicodeString & prefix,const UnicodeString & suffix,const UnicodeString & signSymbol,const UnicodeString & currencySymbol,UChar & currencySeparatorChar)234 ICUMonetaryData::_DetermineCurrencyPosAndSeparator(const UnicodeString& prefix,
235 const UnicodeString& suffix, const UnicodeString& signSymbol,
236 const UnicodeString& currencySymbol, UChar& currencySeparatorChar)
237 {
238 int32 result = 0;
239
240 int32 currencySymbolPos = prefix.indexOf(currencySymbol);
241 if (currencySymbolPos > -1) {
242 result |= kCsPrecedesFlag;
243 int32 signSymbolPos = prefix.indexOf(signSymbol);
244 // if a char is following the currency symbol, we assume it's
245 // the separator (usually space), but we need to take care to
246 // skip over the sign symbol, if found
247 int32 potentialSeparatorPos
248 = currencySymbolPos + currencySymbol.length();
249 if (potentialSeparatorPos == signSymbolPos)
250 potentialSeparatorPos++;
251 if (prefix.charAt(potentialSeparatorPos) != 0xFFFF) {
252 // We can't use the actual separator char since this is usually
253 // 'c2a0' (non-breakable space), which is not available in the
254 // ASCII charset used/assumed by POSIX lconv. So we use space
255 // instead.
256 currencySeparatorChar = ' ';
257 result |= kSepBySpaceFlag;
258 }
259 } else {
260 currencySymbolPos = suffix.indexOf(currencySymbol);
261 if (currencySymbolPos > -1) {
262 int32 signSymbolPos = suffix.indexOf(signSymbol);
263 // if a char is preceding the currency symbol, we assume
264 // it's the separator (usually space), but we need to take
265 // care to skip the sign symbol, if found
266 int32 potentialSeparatorPos = currencySymbolPos - 1;
267 if (potentialSeparatorPos == signSymbolPos)
268 potentialSeparatorPos--;
269 if (suffix.charAt(potentialSeparatorPos) != 0xFFFF) {
270 // We can't use the actual separator char since this is usually
271 // 'c2a0' (non-breakable space), which is not available in the
272 // ASCII charset used/assumed by POSIX lconv. So we use space
273 // instead.
274 currencySeparatorChar = ' ';
275 result |= kSepBySpaceFlag;
276 }
277 }
278 }
279
280 return result;
281 }
282
283
284 /*
285 * This method determines the positive/negative sign position value according to
286 * the following map (where '$' indicated the currency symbol, '#' the number
287 * value, and '-' or parantheses the sign symbol):
288 * ($#) -> 0
289 * (#$) -> 0
290 * -$# -> 1
291 * -#$ -> 1
292 * $-# -> 4
293 * $#- -> 2
294 * #$- -> 2
295 * #-$ -> 3
296 */
297 int32
_DetermineSignPos(const UnicodeString & prefix,const UnicodeString & suffix,const UnicodeString & signSymbol,const UnicodeString & currencySymbol)298 ICUMonetaryData::_DetermineSignPos(const UnicodeString& prefix,
299 const UnicodeString& suffix, const UnicodeString& signSymbol,
300 const UnicodeString& currencySymbol)
301 {
302 if (prefix.indexOf(UnicodeString("(", "")) >= 0
303 && suffix.indexOf(UnicodeString(")", "")) >= 0)
304 return kParenthesesAroundCurrencyAndValue;
305
306 UnicodeString value("#", "");
307 UnicodeString prefixNumberSuffixString = prefix + value + suffix;
308 int32 signSymbolPos = prefixNumberSuffixString.indexOf(signSymbol);
309 if (signSymbolPos >= 0) {
310 int32 valuePos = prefixNumberSuffixString.indexOf(value);
311 int32 currencySymbolPos
312 = prefixNumberSuffixString.indexOf(currencySymbol);
313
314 if (signSymbolPos < valuePos && signSymbolPos < currencySymbolPos)
315 return kSignPrecedesCurrencyAndValue;
316 if (signSymbolPos > valuePos && signSymbolPos > currencySymbolPos)
317 return kSignSucceedsCurrencyAndValue;
318 if (signSymbolPos == currencySymbolPos - 1)
319 return kSignImmediatelyPrecedesCurrency;
320 if (signSymbolPos == currencySymbolPos + currencySymbol.length())
321 return kSignImmediatelySucceedsCurrency;
322 }
323
324 return CHAR_MAX;
325 }
326
327
328 } // namespace Libroot
329 } // namespace BPrivate
330