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