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