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