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 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 39 ICUMonetaryData::Initialize(LocaleMonetaryDataBridge* dataBridge) 40 { 41 fPosixLocaleConv = dataBridge->posixLocaleConv; 42 } 43 44 45 status_t 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 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* 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 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 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