/* * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de. * Distributed under the terms of the MIT License. */ #include "ICUMonetaryData.h" #include #include #include namespace BPrivate { namespace Libroot { ICUMonetaryData::ICUMonetaryData(pthread_key_t tlsKey, struct lconv& localeConv) : inherited(tlsKey), fLocaleConv(localeConv), fPosixLocaleConv(NULL) { fLocaleConv.int_curr_symbol = fIntCurrSymbol; fLocaleConv.currency_symbol = fCurrencySymbol; fLocaleConv.mon_decimal_point = fDecimalPoint; fLocaleConv.mon_thousands_sep = fThousandsSep; fLocaleConv.mon_grouping = fGrouping; fLocaleConv.positive_sign = fPositiveSign; fLocaleConv.negative_sign = fNegativeSign; } void ICUMonetaryData::Initialize(LocaleMonetaryDataBridge* dataBridge) { fPosixLocaleConv = dataBridge->posixLocaleConv; } status_t ICUMonetaryData::SetTo(const Locale& locale, const char* posixLocaleName) { status_t result = inherited::SetTo(locale, posixLocaleName); if (result == B_OK) { UErrorCode icuStatus = U_ZERO_ERROR; UChar intlCurrencySeparatorChar = CHAR_MAX; DecimalFormat* currencyFormat = dynamic_cast( NumberFormat::createCurrencyInstance(locale, icuStatus)); if (!U_SUCCESS(icuStatus)) return B_UNSUPPORTED; if (!currencyFormat) return B_BAD_TYPE; const DecimalFormatSymbols* formatSymbols = currencyFormat->getDecimalFormatSymbols(); if (!formatSymbols) result = B_BAD_DATA; if (result == B_OK) { result = _SetLocaleconvEntry(formatSymbols, fDecimalPoint, DecimalFormatSymbols::kMonetarySeparatorSymbol); } if (result == B_OK) { result = _SetLocaleconvEntry(formatSymbols, fThousandsSep, DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); } if (result == B_OK) { int32 groupingSize = currencyFormat->getGroupingSize(); if (groupingSize < 1) fGrouping[0] = '\0'; else { fGrouping[0] = groupingSize; int32 secondaryGroupingSize = currencyFormat->getSecondaryGroupingSize(); if (secondaryGroupingSize < 1) fGrouping[1] = '\0'; else { fGrouping[1] = secondaryGroupingSize; fGrouping[2] = '\0'; } } } if (result == B_OK) { fLocaleConv.int_frac_digits = currencyFormat->getMinimumFractionDigits(); fLocaleConv.frac_digits = currencyFormat->getMinimumFractionDigits(); } if (result == B_OK) { UnicodeString positivePrefix, positiveSuffix, negativePrefix, negativeSuffix; currencyFormat->getPositivePrefix(positivePrefix); currencyFormat->getPositiveSuffix(positiveSuffix); currencyFormat->getNegativePrefix(negativePrefix); currencyFormat->getNegativeSuffix(negativeSuffix); UnicodeString currencySymbol = formatSymbols->getSymbol( DecimalFormatSymbols::kCurrencySymbol); UnicodeString plusSymbol = formatSymbols->getSymbol( DecimalFormatSymbols::kPlusSignSymbol); UnicodeString minusSymbol = formatSymbols->getSymbol( DecimalFormatSymbols::kMinusSignSymbol); // fill national values int32 positiveCurrencyFlags = _DetermineCurrencyPosAndSeparator( positivePrefix, positiveSuffix, plusSymbol, currencySymbol, intlCurrencySeparatorChar); fLocaleConv.p_cs_precedes = (positiveCurrencyFlags & kCsPrecedesFlag) ? 1 : 0; fLocaleConv.p_sep_by_space = (positiveCurrencyFlags & kSepBySpaceFlag) ? 1 : 0; int32 negativeCurrencyFlags = _DetermineCurrencyPosAndSeparator( negativePrefix, negativeSuffix, minusSymbol, currencySymbol, intlCurrencySeparatorChar); fLocaleConv.n_cs_precedes = (negativeCurrencyFlags & kCsPrecedesFlag) ? 1 : 0; fLocaleConv.n_sep_by_space = (negativeCurrencyFlags & kSepBySpaceFlag) ? 1 : 0; fLocaleConv.p_sign_posn = _DetermineSignPos(positivePrefix, positiveSuffix, plusSymbol, currencySymbol); fLocaleConv.n_sign_posn = _DetermineSignPos(negativePrefix, negativeSuffix, minusSymbol, currencySymbol); if (fLocaleConv.p_sign_posn == CHAR_MAX) { // usually there is no positive sign indicator, so we // adopt the sign pos of the negative sign symbol fLocaleConv.p_sign_posn = fLocaleConv.n_sign_posn; } // copy national to international values, as ICU does not seem // to have separate info for those fLocaleConv.int_p_cs_precedes = fLocaleConv.p_cs_precedes; fLocaleConv.int_p_sep_by_space = fLocaleConv.p_sep_by_space; fLocaleConv.int_n_cs_precedes = fLocaleConv.n_cs_precedes; fLocaleConv.int_n_sep_by_space = fLocaleConv.n_sep_by_space; fLocaleConv.int_p_sign_posn = fLocaleConv.p_sign_posn; fLocaleConv.int_n_sign_posn = fLocaleConv.n_sign_posn; // only set sign symbols if they are actually used in any pattern if (positivePrefix.indexOf(plusSymbol) > -1 || positiveSuffix.indexOf(plusSymbol) > -1) { result = _SetLocaleconvEntry(formatSymbols, fPositiveSign, DecimalFormatSymbols::kPlusSignSymbol); } else fPositiveSign[0] = '\0'; if (negativePrefix.indexOf(minusSymbol) > -1 || negativeSuffix.indexOf(minusSymbol) > -1) { result = _SetLocaleconvEntry(formatSymbols, fNegativeSign, DecimalFormatSymbols::kMinusSignSymbol); } else fNegativeSign[0] = '\0'; } if (result == B_OK) { UnicodeString intlCurrencySymbol = formatSymbols->getSymbol( DecimalFormatSymbols::kIntlCurrencySymbol); if (intlCurrencySeparatorChar != CHAR_MAX) intlCurrencySymbol += intlCurrencySeparatorChar; else intlCurrencySymbol += ' '; result = _ConvertUnicodeStringToLocaleconvEntry(intlCurrencySymbol, fIntCurrSymbol, skLCBufSize); } if (result == B_OK) { result = _SetLocaleconvEntry(formatSymbols, fCurrencySymbol, DecimalFormatSymbols::kCurrencySymbol); if (fCurrencySymbol[0] == '\0') { // fall back to the international currency symbol result = _SetLocaleconvEntry(formatSymbols, fCurrencySymbol, DecimalFormatSymbols::kIntlCurrencySymbol); fCurrencySymbol[3] = '\0'; // drop separator char that's contained in int-curr-symbol } } delete currencyFormat; } return result; } status_t ICUMonetaryData::SetToPosix() { status_t result = inherited::SetToPosix(); if (result == B_OK) { strcpy(fDecimalPoint, fPosixLocaleConv->mon_decimal_point); strcpy(fThousandsSep, fPosixLocaleConv->mon_thousands_sep); strcpy(fGrouping, fPosixLocaleConv->mon_grouping); strcpy(fCurrencySymbol, fPosixLocaleConv->currency_symbol); strcpy(fIntCurrSymbol, fPosixLocaleConv->int_curr_symbol); strcpy(fPositiveSign, fPosixLocaleConv->positive_sign); strcpy(fNegativeSign, fPosixLocaleConv->negative_sign); fLocaleConv.int_frac_digits = fPosixLocaleConv->int_frac_digits; fLocaleConv.frac_digits = fPosixLocaleConv->frac_digits; fLocaleConv.p_cs_precedes = fPosixLocaleConv->p_cs_precedes; fLocaleConv.p_sep_by_space = fPosixLocaleConv->p_sep_by_space; fLocaleConv.n_cs_precedes = fPosixLocaleConv->n_cs_precedes; fLocaleConv.n_sep_by_space = fPosixLocaleConv->n_sep_by_space; fLocaleConv.p_sign_posn = fPosixLocaleConv->p_sign_posn; fLocaleConv.n_sign_posn = fPosixLocaleConv->n_sign_posn; fLocaleConv.int_p_cs_precedes = fPosixLocaleConv->int_p_cs_precedes; fLocaleConv.int_p_sep_by_space = fPosixLocaleConv->int_p_sep_by_space; fLocaleConv.int_n_cs_precedes = fPosixLocaleConv->int_n_cs_precedes; fLocaleConv.int_n_sep_by_space = fPosixLocaleConv->int_n_sep_by_space; fLocaleConv.int_p_sign_posn = fPosixLocaleConv->int_p_sign_posn; fLocaleConv.int_n_sign_posn = fPosixLocaleConv->int_n_sign_posn; } return result; } const char* ICUMonetaryData::GetLanginfo(int index) { switch(index) { case CRNCYSTR: return fCurrencySymbol; default: return ""; } } int32 ICUMonetaryData::_DetermineCurrencyPosAndSeparator(const UnicodeString& prefix, const UnicodeString& suffix, const UnicodeString& signSymbol, const UnicodeString& currencySymbol, UChar& currencySeparatorChar) { int32 result = 0; int32 currencySymbolPos = prefix.indexOf(currencySymbol); if (currencySymbolPos > -1) { result |= kCsPrecedesFlag; int32 signSymbolPos = prefix.indexOf(signSymbol); // if a char is following the currency symbol, we assume it's // the separator (usually space), but we need to take care to // skip over the sign symbol, if found int32 potentialSeparatorPos = currencySymbolPos + currencySymbol.length(); if (potentialSeparatorPos == signSymbolPos) potentialSeparatorPos++; if (prefix.charAt(potentialSeparatorPos) != 0xFFFF) { // We can't use the actual separator char since this is usually // 'c2a0' (non-breakable space), which is not available in the // ASCII charset used/assumed by POSIX lconv. So we use space // instead. currencySeparatorChar = ' '; result |= kSepBySpaceFlag; } } else { currencySymbolPos = suffix.indexOf(currencySymbol); if (currencySymbolPos > -1) { int32 signSymbolPos = suffix.indexOf(signSymbol); // if a char is preceding the currency symbol, we assume // it's the separator (usually space), but we need to take // care to skip the sign symbol, if found int32 potentialSeparatorPos = currencySymbolPos - 1; if (potentialSeparatorPos == signSymbolPos) potentialSeparatorPos--; if (suffix.charAt(potentialSeparatorPos) != 0xFFFF) { // We can't use the actual separator char since this is usually // 'c2a0' (non-breakable space), which is not available in the // ASCII charset used/assumed by POSIX lconv. So we use space // instead. currencySeparatorChar = ' '; result |= kSepBySpaceFlag; } } } return result; } /* * This method determines the positive/negative sign position value according to * the following map (where '$' indicated the currency symbol, '#' the number * value, and '-' or parantheses the sign symbol): * ($#) -> 0 * (#$) -> 0 * -$# -> 1 * -#$ -> 1 * $-# -> 4 * $#- -> 2 * #$- -> 2 * #-$ -> 3 */ int32 ICUMonetaryData::_DetermineSignPos(const UnicodeString& prefix, const UnicodeString& suffix, const UnicodeString& signSymbol, const UnicodeString& currencySymbol) { if (prefix.indexOf(UnicodeString("(", "")) >= 0 && suffix.indexOf(UnicodeString(")", "")) >= 0) return kParenthesesAroundCurrencyAndValue; UnicodeString value("#", ""); UnicodeString prefixNumberSuffixString = prefix + value + suffix; int32 signSymbolPos = prefixNumberSuffixString.indexOf(signSymbol); if (signSymbolPos >= 0) { int32 valuePos = prefixNumberSuffixString.indexOf(value); int32 currencySymbolPos = prefixNumberSuffixString.indexOf(currencySymbol); if (signSymbolPos < valuePos && signSymbolPos < currencySymbolPos) return kSignPrecedesCurrencyAndValue; if (signSymbolPos > valuePos && signSymbolPos > currencySymbolPos) return kSignSucceedsCurrencyAndValue; if (signSymbolPos == currencySymbolPos - 1) return kSignImmediatelyPrecedesCurrency; if (signSymbolPos == currencySymbolPos + currencySymbol.length()) return kSignImmediatelySucceedsCurrency; } return CHAR_MAX; } } // namespace Libroot } // namespace BPrivate