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