| /* CFNumberFormatter.c |
| Copyright (c) 2002-2016, Apple Inc. and the Swift project authors |
| |
| Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors |
| Licensed under Apache License v2.0 with Runtime Library Exception |
| See http://swift.org/LICENSE.txt for license information |
| See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| Responsibility: David Smith |
| */ |
| |
| #include <CoreFoundation/CFNumberFormatter.h> |
| #include <CoreFoundation/ForFoundationOnly.h> |
| #include <CoreFoundation/CFBigNumber.h> |
| #include "CFInternal.h" |
| #include "CFLocaleInternal.h" |
| #include "CFICULogging.h" |
| #include <math.h> |
| #include <float.h> |
| |
| |
| static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter); |
| static CFStringRef __CFNumberFormatterCreateCompressedString(CFStringRef inString, Boolean isFormat, CFRange *rangep); |
| static UErrorCode __CFNumberFormatterApplyPattern(CFNumberFormatterRef formatter, CFStringRef pattern); |
| CONST_STRING_DECL(kCFNumberFormatterFormattingContextKey, "kCFNumberFormatterFormattingContextKey"); |
| |
| #define BUFFER_SIZE 768 |
| |
| struct __CFNumberFormatter { |
| CFRuntimeBase _base; |
| UNumberFormat *_nf; |
| CFLocaleRef _locale; |
| CFNumberFormatterStyle _style; |
| CFStringRef _format; // NULL for RBNFs |
| CFStringRef _defformat; |
| CFStringRef _compformat; |
| CFNumberRef _multiplier; |
| CFStringRef _zeroSym; |
| Boolean _isLenient; |
| Boolean _userSetMultiplier; |
| Boolean _usesCharacterDirection; |
| }; |
| |
| static CFStringRef __CFNumberFormatterCopyDescription(CFTypeRef cf) { |
| CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf; |
| return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFNumberFormatter %p [%p]>"), cf, CFGetAllocator(formatter)); |
| } |
| |
| static void __CFNumberFormatterDeallocate(CFTypeRef cf) { |
| CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf; |
| if (formatter->_nf) __cficu_unum_close(formatter->_nf); |
| if (formatter->_locale) CFRelease(formatter->_locale); |
| if (formatter->_format) CFRelease(formatter->_format); |
| if (formatter->_defformat) CFRelease(formatter->_defformat); |
| if (formatter->_compformat) CFRelease(formatter->_compformat); |
| if (formatter->_multiplier) CFRelease(formatter->_multiplier); |
| if (formatter->_zeroSym) CFRelease(formatter->_zeroSym); |
| } |
| |
| static CFTypeID __kCFNumberFormatterTypeID = _kCFRuntimeNotATypeID; |
| |
| static const CFRuntimeClass __CFNumberFormatterClass = { |
| 0, |
| "CFNumberFormatter", |
| NULL, // init |
| NULL, // copy |
| __CFNumberFormatterDeallocate, |
| NULL, |
| NULL, |
| NULL, // |
| __CFNumberFormatterCopyDescription |
| }; |
| |
| CFTypeID CFNumberFormatterGetTypeID(void) { |
| static dispatch_once_t initOnce; |
| dispatch_once(&initOnce, ^{ __kCFNumberFormatterTypeID = _CFRuntimeRegisterClass(&__CFNumberFormatterClass); }); |
| return __kCFNumberFormatterTypeID; |
| } |
| |
| CFNumberFormatterRef CFNumberFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFNumberFormatterStyle style) { |
| struct __CFNumberFormatter *memory; |
| uint32_t size = sizeof(struct __CFNumberFormatter) - sizeof(CFRuntimeBase); |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(locale, CFLocaleGetTypeID()); |
| memory = (struct __CFNumberFormatter *)_CFRuntimeCreateInstance(allocator, CFNumberFormatterGetTypeID(), size, NULL); |
| if (NULL == memory) { |
| return NULL; |
| } |
| memory->_nf = NULL; |
| memory->_locale = NULL; |
| memory->_format = NULL; |
| memory->_defformat = NULL; |
| memory->_compformat = NULL; |
| memory->_multiplier = NULL; |
| memory->_zeroSym = NULL; |
| memory->_isLenient = false; |
| memory->_userSetMultiplier = false; |
| memory->_usesCharacterDirection = false; |
| if (NULL == locale) locale = CFLocaleGetSystem(); |
| memory->_style = style; |
| uint32_t ustyle; |
| switch (style) { |
| case kCFNumberFormatterNoStyle: ustyle = UNUM_IGNORE; break; |
| case kCFNumberFormatterDecimalStyle: ustyle = UNUM_DECIMAL; break; |
| case kCFNumberFormatterCurrencyStyle: ustyle = UNUM_CURRENCY; break; |
| case kCFNumberFormatterPercentStyle: ustyle = UNUM_PERCENT; break; |
| case kCFNumberFormatterScientificStyle: ustyle = UNUM_SCIENTIFIC; break; |
| case kCFNumberFormatterSpellOutStyle: ustyle = UNUM_SPELLOUT; break; |
| case kCFNumberFormatterOrdinalStyle: ustyle = UNUM_ORDINAL; break; |
| case kCFNumberFormatterDurationStyle: ustyle = UNUM_DURATION; break; |
| case kCFNumberFormatterCurrencyISOCodeStyle: ustyle = UNUM_CURRENCY_ISO; break; |
| case kCFNumberFormatterCurrencyPluralStyle: ustyle = UNUM_CURRENCY_PLURAL; break; |
| #if U_ICU_VERSION_MAJOR_NUM >= 55 |
| case kCFNumberFormatterCurrencyAccountingStyle: ustyle = UNUM_CURRENCY_ACCOUNTING; break; |
| #endif |
| default: |
| CFAssert2(0, __kCFLogAssertion, "%s(): unknown style %ld", __PRETTY_FUNCTION__, style); |
| ustyle = UNUM_DECIMAL; |
| memory->_style = kCFNumberFormatterDecimalStyle; |
| break; |
| } |
| CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR(""); |
| char buffer[BUFFER_SIZE]; |
| const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); |
| if (NULL == cstr) { |
| if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; |
| } |
| if (NULL == cstr) { |
| CFRelease(memory); |
| return NULL; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| memory->_nf = __cficu_unum_open((UNumberFormatStyle)ustyle, NULL, 0, cstr, NULL, &status); |
| CFAssert2(memory->_nf, __kCFLogAssertion, "%s(): error (%d) creating number formatter", __PRETTY_FUNCTION__, status); |
| if (NULL == memory->_nf) { |
| CFRelease(memory); |
| return NULL; |
| } |
| UChar ubuff[4]; |
| if (kCFNumberFormatterNoStyle == style) { |
| status = U_ZERO_ERROR; |
| ubuff[0] = '#'; ubuff[1] = ';'; ubuff[2] = '#'; |
| __cficu_unum_applyPattern(memory->_nf, false, ubuff, 3, NULL, &status); |
| __cficu_unum_setAttribute(memory->_nf, UNUM_MAX_INTEGER_DIGITS, 42); |
| __cficu_unum_setAttribute(memory->_nf, UNUM_MAX_FRACTION_DIGITS, 0); |
| } |
| //Prior to Gala, CFLocaleCreateCopy() always just retained. This caused problems because CFLocaleGetValue(locale, kCFLocaleCalendarKey) would create a calendar, then set its locale to self, leading to a retain cycle |
| //Since we're not in that situation here, and this is a frequently used path, we retain as we used to |
| memory->_locale = locale ? CFRetain(locale) : CFLocaleGetSystem(); |
| __CFNumberFormatterCustomize(memory); |
| if (kCFNumberFormatterSpellOutStyle != memory->_style && kCFNumberFormatterOrdinalStyle != memory->_style && kCFNumberFormatterCurrencyPluralStyle != memory->_style && kCFNumberFormatterDurationStyle != memory->_style) { |
| UChar ubuffer[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| int32_t ret = __cficu_unum_toPattern(memory->_nf, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { |
| memory->_format = CFStringCreateWithCharacters(allocator, (const UniChar *)ubuffer, ret); |
| } |
| } |
| memory->_defformat = memory->_format ? (CFStringRef)CFRetain(memory->_format) : NULL; |
| memory->_compformat = memory->_format ? __CFNumberFormatterCreateCompressedString(memory->_format, true, NULL) : NULL; |
| if (kCFNumberFormatterSpellOutStyle != memory->_style && kCFNumberFormatterOrdinalStyle != memory->_style && kCFNumberFormatterCurrencyPluralStyle != memory->_style && kCFNumberFormatterDurationStyle != memory->_style) { |
| int32_t n = __cficu_unum_getAttribute(memory->_nf, UNUM_MULTIPLIER); |
| if (1 != n) { |
| memory->_multiplier = CFNumberCreate(allocator, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(memory->_nf, UNUM_MULTIPLIER, 1); |
| } |
| } |
| __cficu_unum_setAttribute(memory->_nf, UNUM_LENIENT_PARSE, 0); |
| #if U_ICU_VERSION_MAJOR_NUM >= 53 |
| __cficu_unum_setContext(memory->_nf, UDISPCTX_CAPITALIZATION_NONE, &status); |
| #endif |
| return (CFNumberFormatterRef)memory; |
| } |
| |
| extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); |
| |
| static void __substituteFormatStringFromPrefsNF(CFNumberFormatterRef formatter) { |
| CFIndex formatStyle = formatter->_style; |
| if (kCFNumberFormatterSpellOutStyle == formatStyle) return; |
| if (kCFNumberFormatterOrdinalStyle == formatStyle) return; |
| if (kCFNumberFormatterDurationStyle == formatStyle) return; |
| if (kCFNumberFormatterCurrencyPluralStyle == formatStyle) return; |
| CFStringRef prefName = CFSTR("AppleICUNumberFormatStrings"); |
| if (kCFNumberFormatterNoStyle != formatStyle) { |
| CFStringRef pref = NULL; |
| CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); |
| CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, prefName) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| CFStringRef key; |
| switch (formatStyle) { |
| case kCFNumberFormatterDecimalStyle: key = CFSTR("1"); break; |
| case kCFNumberFormatterCurrencyStyle: key = CFSTR("2"); break; |
| case kCFNumberFormatterPercentStyle: key = CFSTR("3"); break; |
| case kCFNumberFormatterScientificStyle: key = CFSTR("4"); break; |
| case kCFNumberFormatterSpellOutStyle: key = CFSTR("5"); break; |
| case kCFNumberFormatterOrdinalStyle: key = CFSTR("6"); break; |
| case kCFNumberFormatterDurationStyle: key = CFSTR("7"); break; |
| case kCFNumberFormatterCurrencyISOCodeStyle: key = CFSTR("8"); break; |
| case kCFNumberFormatterCurrencyPluralStyle: key = CFSTR("9"); break; |
| #if U_ICU_VERSION_MAJOR_NUM >= 55 |
| case kCFNumberFormatterCurrencyAccountingStyle: key = CFSTR("10"); break; |
| #endif |
| default: key = CFSTR("0"); break; |
| } |
| pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); |
| } |
| if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { |
| int32_t icustyle = UNUM_IGNORE; |
| switch (formatStyle) { |
| case kCFNumberFormatterDecimalStyle: icustyle = UNUM_DECIMAL; break; |
| case kCFNumberFormatterCurrencyStyle: icustyle = UNUM_CURRENCY; break; |
| case kCFNumberFormatterPercentStyle: icustyle = UNUM_PERCENT; break; |
| case kCFNumberFormatterScientificStyle: icustyle = UNUM_SCIENTIFIC; break; |
| case kCFNumberFormatterSpellOutStyle: icustyle = UNUM_SPELLOUT; break; |
| case kCFNumberFormatterOrdinalStyle: icustyle = UNUM_ORDINAL; break; |
| case kCFNumberFormatterDurationStyle: icustyle = UNUM_DURATION; break; |
| case kCFNumberFormatterCurrencyISOCodeStyle: icustyle = UNUM_CURRENCY_ISO; break; |
| case kCFNumberFormatterCurrencyPluralStyle: icustyle = UNUM_CURRENCY_PLURAL; break; |
| #if U_ICU_VERSION_MAJOR_NUM >= 55 |
| case kCFNumberFormatterCurrencyAccountingStyle: icustyle = UNUM_CURRENCY_ACCOUNTING; break; |
| #endif |
| } |
| CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); |
| char buffer[BUFFER_SIZE]; |
| const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); |
| if (NULL == cstr) { |
| if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| UNumberFormat *nf = __cficu_unum_open((UNumberFormatStyle)icustyle, NULL, 0, cstr, NULL, &status); |
| if (NULL != nf) { |
| UChar ubuffer[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| int32_t number_len = __cficu_unum_toPattern(nf, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && number_len <= BUFFER_SIZE) { |
| CFStringRef numberString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, number_len); |
| status = U_ZERO_ERROR; |
| int32_t formatter_len = __cficu_unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) { |
| CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
| CFStringAppendCharacters(formatString, (const UniChar *)ubuffer, formatter_len); |
| // find numberString inside formatString, substitute the pref in that range |
| CFRange result; |
| if (CFStringFindWithOptions(formatString, numberString, CFRangeMake(0, formatter_len), 0, &result)) { |
| CFStringReplace(formatString, result, pref); |
| __CFNumberFormatterApplyPattern(formatter, formatString); |
| } |
| CFRelease(formatString); |
| } |
| CFRelease(numberString); |
| } |
| __cficu_unum_close(nf); |
| } |
| } |
| } |
| } |
| |
| static UniChar __CFNumberFormatterNormalizeCharacter(UniChar c) { |
| if (CFCharacterSetIsCharacterMember(CFCharacterSetGetPredefined(kCFCharacterSetWhitespace), c)) { |
| return ' '; |
| } else { |
| return c; |
| } |
| } |
| |
| /* Attempt to match the unimplemented lenient parsing behavior described at http://www.unicode.org/reports/tr35/#Lenient_Parsing -- specifically any whitespace is ignored that does not exist between two letters or two numbers, and no-break spaces map to spaces. */ |
| static CFStringRef __CFNumberFormatterCreateCompressedString(CFStringRef inString, Boolean isFormat, CFRange *rangep) { |
| if (!inString) return NULL; |
| CFRange range = { 0, 0 }; |
| if (rangep) { |
| range = *rangep; |
| } else { |
| range.length = CFStringGetLength(inString); |
| } |
| CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
| CFCharacterSetRef letters = CFCharacterSetGetPredefined(kCFCharacterSetLetter); |
| CFCharacterSetRef numbers = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); |
| UniChar prevCh = 0, nextCh = 0; |
| Boolean inQuote = false; |
| for (CFIndex in_idx = range.location; in_idx < range.location + range.length; in_idx++) { |
| UniChar ch = __CFNumberFormatterNormalizeCharacter(CFStringGetCharacterAtIndex(inString, in_idx)); |
| nextCh = (in_idx+1 < range.length) ? CFStringGetCharacterAtIndex(inString, in_idx+1) : 0; |
| if (isFormat && ch == '\'') inQuote = !inQuote; |
| if (inQuote || ch != ' ' || (CFCharacterSetIsCharacterMember(letters, prevCh) && CFCharacterSetIsCharacterMember(letters, nextCh)) || (CFCharacterSetIsCharacterMember(numbers, prevCh) && CFCharacterSetIsCharacterMember(numbers, nextCh))) { |
| CFStringAppendCharacters(outString, &ch, 1); |
| prevCh = ch; |
| } |
| } |
| return outString; |
| } |
| |
| // Should not be called for rule-based ICU formatters; not supported |
| static void __CFNumberFormatterApplySymbolPrefs(const void *key, const void *value, void *context) { |
| if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFStringGetTypeID()) { |
| CFNumberFormatterRef formatter = (CFNumberFormatterRef)context; |
| UNumberFormatSymbol sym = (UNumberFormatSymbol)CFStringGetIntValue((CFStringRef)key); |
| CFStringRef item = (CFStringRef)value; |
| CFIndex item_cnt = CFStringGetLength(item); |
| STACK_BUFFER_DECL(UChar, item_buffer, item_cnt); |
| UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); |
| if (NULL == item_ustr) { |
| CFStringGetCharacters(item, CFRangeMake(0, __CFMin(BUFFER_SIZE, item_cnt)), (UniChar *)item_buffer); |
| item_ustr = item_buffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| __cficu_unum_setSymbol(formatter->_nf, sym, item_ustr, item_cnt, &status); |
| } |
| } |
| |
| // Should not be called for rule-based ICU formatters |
| static UErrorCode __CFNumberFormatterApplyPattern(CFNumberFormatterRef formatter, CFStringRef pattern) { |
| if (kCFNumberFormatterSpellOutStyle == formatter->_style) return U_UNSUPPORTED_ERROR; |
| if (kCFNumberFormatterOrdinalStyle == formatter->_style) return U_UNSUPPORTED_ERROR; |
| if (kCFNumberFormatterDurationStyle == formatter->_style) return U_UNSUPPORTED_ERROR; |
| if (kCFNumberFormatterCurrencyPluralStyle == formatter->_style) return U_UNSUPPORTED_ERROR; |
| CFIndex cnt = CFStringGetLength(pattern); |
| STACK_BUFFER_DECL(UChar, ubuffer, cnt); |
| const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(pattern); |
| if (NULL == ustr) { |
| CFStringGetCharacters(pattern, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| __cficu_unum_applyPattern(formatter->_nf, false, ustr, cnt, NULL, &status); |
| |
| // __cficu_unum_applyPattern() may have magically changed other attributes based on |
| // the contents of the format string; we simply expose that ICU behavior, except |
| // for UNUM_MULTIPLIER, which we re-read and reset, like we did at initialization |
| // time though any user-set multiplier state takes precedence. |
| if (formatter->_userSetMultiplier) { |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MULTIPLIER, 1); |
| } else { |
| if (formatter->_multiplier) CFRelease(formatter->_multiplier); |
| formatter->_multiplier = NULL; |
| int32_t n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MULTIPLIER); |
| if (1 != n) { |
| formatter->_multiplier = CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MULTIPLIER, 1); |
| } |
| } |
| return status; |
| } |
| |
| static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter) { |
| __substituteFormatStringFromPrefsNF(formatter); |
| CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); |
| CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUNumberSymbols")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFNumberFormatterApplySymbolPrefs, formatter); |
| } |
| } |
| |
| CFLocaleRef CFNumberFormatterGetLocale(CFNumberFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| return formatter->_locale; |
| } |
| |
| CFNumberFormatterStyle CFNumberFormatterGetStyle(CFNumberFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| return formatter->_style; |
| } |
| |
| CFStringRef CFNumberFormatterGetFormat(CFNumberFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| if (kCFNumberFormatterSpellOutStyle == formatter->_style) return NULL; |
| if (kCFNumberFormatterOrdinalStyle == formatter->_style) return NULL; |
| if (kCFNumberFormatterDurationStyle == formatter->_style) return NULL; |
| if (kCFNumberFormatterCurrencyPluralStyle == formatter->_style) return NULL; |
| UChar ubuffer[BUFFER_SIZE]; |
| CFStringRef newString = NULL; |
| UErrorCode status = U_ZERO_ERROR; |
| int32_t ret = __cficu_unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { |
| newString = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ret); |
| } |
| if (newString && !formatter->_format) { |
| formatter->_format = newString; |
| if (formatter->_compformat) CFRelease(formatter->_compformat); |
| formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL); |
| } else if (newString && !CFEqual(newString, formatter->_format)) { |
| CFRelease(formatter->_format); |
| formatter->_format = newString; |
| if (formatter->_compformat) CFRelease(formatter->_compformat); |
| formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL); |
| } else if (newString) { |
| CFRelease(newString); |
| } |
| return formatter->_format; |
| } |
| |
| void CFNumberFormatterSetFormat(CFNumberFormatterRef formatter, CFStringRef formatString) { |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| __CFGenericValidateType(formatString, CFStringGetTypeID()); |
| if (kCFNumberFormatterSpellOutStyle == formatter->_style) return; |
| if (kCFNumberFormatterOrdinalStyle == formatter->_style) return; |
| if (kCFNumberFormatterDurationStyle == formatter->_style) return; |
| if (kCFNumberFormatterCurrencyPluralStyle == formatter->_style) return; |
| CFIndex cnt = CFStringGetLength(formatString); |
| CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); |
| if ((!formatter->_format || !CFEqual(formatter->_format, formatString)) && cnt <= 1024) { |
| UErrorCode status = __CFNumberFormatterApplyPattern(formatter, formatString); |
| if (U_SUCCESS(status)) { |
| UChar ubuffer2[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| int32_t ret = __cficu_unum_toPattern(formatter->_nf, false, ubuffer2, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { |
| if (formatter->_format) CFRelease(formatter->_format); |
| formatter->_format = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer2, ret); |
| if (formatter->_compformat) CFRelease(formatter->_compformat); |
| formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL); |
| } |
| } |
| } |
| } |
| |
| #define GET_MULTIPLIER \ |
| double multiplier = 1.0; \ |
| double dummy = 0.0; \ |
| if (formatter->_multiplier) { \ |
| if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { \ |
| multiplier = 1.0; \ |
| } \ |
| } \ |
| if (modf(multiplier, &dummy) < FLT_EPSILON) { \ |
| multiplier = floor(multiplier); \ |
| } |
| |
| CFStringRef CFNumberFormatterCreateStringWithNumber(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberRef number) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| __CFGenericValidateType(number, CFNumberGetTypeID()); |
| // The values of CFNumbers with large unsigned 64-bit ints don't survive well through this |
| CFNumberType type = CFNumberGetType(number); |
| char buffer[64]; |
| CFNumberGetValue(number, type, buffer); |
| return CFNumberFormatterCreateStringWithValue(allocator, formatter, type, buffer); |
| } |
| |
| #define FORMAT_FLT(T, FUNC) \ |
| T value;\ |
| memcpy(&value, valuePtr, sizeof(T));\ |
| if (0 == value && formatter->_zeroSym) { return (CFStringRef)CFRetain(formatter->_zeroSym); } \ |
| if (1.0 != multiplier) { \ |
| value = (T)(value * multiplier); \ |
| } \ |
| status = U_ZERO_ERROR; \ |
| used = FUNC(formatter->_nf, value, ubuffer + 1, cnt, NULL, &status); \ |
| if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { \ |
| cnt = used + 1 + 1; \ |
| ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); \ |
| status = U_ZERO_ERROR; \ |
| used = FUNC(formatter->_nf, value, ustr + 1, cnt, NULL, &status); \ |
| } |
| |
| #define FORMAT_INT(T, FUN) \ |
| T value;\ |
| memcpy(&value, valuePtr, sizeof(T));\ |
| if (0 == value && formatter->_zeroSym) { return (CFStringRef)CFRetain(formatter->_zeroSym); } \ |
| if (1.0 != multiplier) { \ |
| value = (T)(value * multiplier); \ |
| } \ |
| _CFBigNum bignum; \ |
| FUN(&bignum, value); \ |
| char buffer[BUFFER_SIZE + 1]; \ |
| _CFBigNumToCString(&bignum, false, true, buffer, BUFFER_SIZE); \ |
| status = U_ZERO_ERROR; \ |
| used = __cficu_unum_formatDecimal(formatter->_nf, buffer, strlen(buffer), ubuffer + 1, BUFFER_SIZE, NULL, &status); \ |
| if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { \ |
| cnt = used + 1 + 1; \ |
| ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); \ |
| status = U_ZERO_ERROR; \ |
| used = __cficu_unum_formatDecimal(formatter->_nf, buffer, strlen(buffer), ustr + 1, cnt, NULL, &status); \ |
| } \ |
| |
| CFStringRef CFNumberFormatterCreateStringWithValue(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberType numberType, const void *valuePtr) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| GET_MULTIPLIER; |
| UChar *ustr = NULL, ubuffer[BUFFER_SIZE + 1]; |
| UErrorCode status = U_ZERO_ERROR; |
| CFIndex used, cnt = BUFFER_SIZE; |
| if (numberType == kCFNumberFloat64Type || numberType == kCFNumberDoubleType) { |
| FORMAT_FLT(double, __cficu_unum_formatDouble) |
| } else if (numberType == kCFNumberFloat32Type || numberType == kCFNumberFloatType) { |
| FORMAT_FLT(float, __cficu_unum_formatDouble) |
| } else if (numberType == kCFNumberSInt64Type || numberType == kCFNumberLongLongType) { |
| FORMAT_INT(int64_t, _CFBigNumInitWithInt64) |
| } else if (numberType == kCFNumberLongType || numberType == kCFNumberCFIndexType) { |
| #if __LP64__ |
| FORMAT_INT(int64_t, _CFBigNumInitWithInt64) |
| #else |
| FORMAT_INT(int32_t, _CFBigNumInitWithInt32) |
| #endif |
| } else if (numberType == kCFNumberSInt32Type || numberType == kCFNumberIntType) { |
| FORMAT_INT(int32_t, _CFBigNumInitWithInt32) |
| } else if (numberType == kCFNumberSInt16Type || numberType == kCFNumberShortType) { |
| FORMAT_INT(int16_t, _CFBigNumInitWithInt16) |
| } else if (numberType == kCFNumberSInt8Type || numberType == kCFNumberCharType) { |
| FORMAT_INT(int8_t, _CFBigNumInitWithInt8) |
| } else { |
| CFAssert2(0, __kCFLogAssertion, "%s(): unknown CFNumberType (%ld)", __PRETTY_FUNCTION__, numberType); |
| return NULL; |
| } |
| CFStringRef string = NULL; |
| if (U_SUCCESS(status)) { |
| UniChar *bufferToUse = ustr ? (UniChar *)ustr : (UniChar *)ubuffer; |
| if (formatter->_usesCharacterDirection && CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter->_locale)) == kCFLocaleLanguageDirectionRightToLeft) { |
| // Insert Unicode RTL marker |
| bufferToUse[0] = 0x200F; |
| used++; |
| } else { |
| // Move past direction marker |
| bufferToUse++; |
| } |
| string = CFStringCreateWithCharacters(allocator, bufferToUse, used); |
| } |
| if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); |
| return string; |
| } |
| |
| #undef FORMAT_FLT |
| #undef FORMAT_INT |
| #undef GET_MULTIPLIER |
| |
| CFNumberRef CFNumberFormatterCreateNumberFromString(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFOptionFlags options) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| __CFGenericValidateType(string, CFStringGetTypeID()); |
| char buffer[16] __attribute__ ((aligned (8))); |
| CFRange r = rangep ? *rangep : CFRangeMake(0, CFStringGetLength(string)); |
| CFNumberRef multiplierRef = formatter->_multiplier; |
| formatter->_multiplier = NULL; |
| Boolean b = CFNumberFormatterGetValueFromString(formatter, string, &r, kCFNumberSInt64Type, buffer); |
| formatter->_multiplier = multiplierRef; |
| if (b) { |
| Boolean passedMultiplier = true; |
| // We handle the multiplier case here manually; the final |
| // result is supposed to be (parsed value) / (multiplier), but |
| // the int case here should only succeed if the parsed value |
| // is an exact multiple of the multiplier. |
| if (multiplierRef) { |
| int64_t tmp = *(int64_t *)buffer; |
| double multiplier = 1.0; |
| if (!CFNumberGetValue(multiplierRef, kCFNumberFloat64Type, &multiplier)) { |
| multiplier = 1.0; |
| } |
| double dummy; |
| if (llabs(tmp) < fabs(multiplier)) { |
| passedMultiplier = false; |
| } else if (fabs(multiplier) < 1.0) { // We can't handle this math yet |
| passedMultiplier = false; |
| } else if (modf(multiplier, &dummy) == 0.0) { // multiplier is an integer |
| int64_t imult = (int64_t)multiplier; |
| int64_t rem = tmp % imult; |
| if (rem != 0) passedMultiplier = false; |
| if (passedMultiplier) { |
| tmp = tmp / imult; |
| *(int64_t *)buffer = tmp; |
| } |
| } else if (multiplier == -1.0) { // simple |
| tmp = tmp * -1; |
| *(int64_t *)buffer = tmp; |
| } else if (multiplier != 1.0) { |
| // First, throw away integer multiples of the multiplier to |
| // bring the value down to less than 2^53, so that we can |
| // cast it to double without losing any precision, important |
| // for the "remainder is zero" test. |
| // Find power of two which, when multiplier is multiplied by it, |
| // results in an integer value. pwr will be <= 52 since multiplier |
| // is at least 1. |
| int pwr = 0; |
| double intgrl; |
| while (modf(scalbn(multiplier, pwr), &intgrl) != 0.0) pwr++; |
| int64_t i2 = (int64_t)intgrl; |
| // scale pwr and i2 up to a reasonably large value so the next loop doesn't take forever |
| while (llabs(i2) < (1LL << 50)) { i2 *= 2; pwr++; } |
| int64_t cnt = 0; |
| while ((1LL << 53) <= llabs(tmp)) { |
| // subtract (multiplier * 2^pwr) each time |
| tmp -= i2; // move tmp toward zero |
| cnt += (1LL << pwr); // remember how many 2^pwr we subtracted |
| } |
| // If the integer is less than 2^53, there is no loss |
| // in converting it to double, so we can just do the |
| // direct operation now. |
| double rem = fmod((double)tmp, multiplier); |
| if (rem != 0.0) passedMultiplier = false; |
| if (passedMultiplier) { |
| // The original tmp, which we need to divide by multiplier, is at this point: |
| // tmp + k * 2^n * multiplier, where k is the number of loop iterations |
| // That original value needs to be divided by multiplier and put back in the |
| // buffer. Noting that k * 2^n == cnt, and after some algebra, we have: |
| tmp = (int64_t)((double)tmp / multiplier) + cnt; |
| *(int64_t *)buffer = tmp; |
| } |
| } |
| } |
| if (passedMultiplier && ((r.length == CFStringGetLength(string)) || (options & kCFNumberFormatterParseIntegersOnly))) { |
| if (rangep) *rangep = r; |
| return CFNumberCreate(allocator, kCFNumberSInt64Type, buffer); |
| } |
| } |
| if (options & kCFNumberFormatterParseIntegersOnly) return NULL; |
| if (CFNumberFormatterGetValueFromString(formatter, string, rangep, kCFNumberFloat64Type, buffer)) { |
| return CFNumberCreate(allocator, kCFNumberFloat64Type, buffer); |
| } |
| return NULL; |
| } |
| |
| Boolean CFNumberFormatterGetValueFromString(CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFNumberType numberType, void *valuePtr) { |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| __CFGenericValidateType(string, CFStringGetTypeID()); |
| CFStringRef stringToParse = formatter->_isLenient ? __CFNumberFormatterCreateCompressedString(string, false, rangep) : (CFStringRef)CFRetain(string); |
| CFRange range = {0, 0}; |
| if(formatter->_isLenient) { |
| range.length = CFStringGetLength(stringToParse); |
| } else { |
| if (rangep) { |
| range = *rangep; |
| } else { |
| range.length = CFStringGetLength(stringToParse); |
| } |
| // __cficu_unum_parse chokes on leading whitespace |
| CFCharacterSetRef whitespace = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace); |
| while(range.length > 0 && CFCharacterSetIsCharacterMember(whitespace, CFStringGetCharacterAtIndex(stringToParse, range.location))) { |
| range.location++; |
| range.length--; |
| } |
| } |
| Boolean isZero = false; |
| if (formatter->_zeroSym) { |
| CFStringRef zeroSym = formatter->_isLenient ? __CFNumberFormatterCreateCompressedString(formatter->_zeroSym, false, NULL) : (CFStringRef)CFRetain(formatter->_zeroSym); |
| if (kCFCompareEqualTo == CFStringCompare(stringToParse, zeroSym, 0)) { |
| isZero = true; |
| } |
| CFRelease(zeroSym); |
| } |
| if (1024 < range.length) range.length = 1024; |
| const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(stringToParse); |
| STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1); |
| if (NULL == ustr) { |
| CFStringGetCharacters(stringToParse, range, (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } else if (!formatter->_isLenient) { |
| ustr += range.location; |
| } |
| CFNumberRef multiplierRef = formatter->_multiplier; |
| formatter->_multiplier = NULL; |
| if (formatter->_isLenient) { |
| __CFNumberFormatterApplyPattern(formatter, formatter->_compformat); |
| if (formatter->_multiplier) CFRelease(formatter->_multiplier); |
| formatter->_multiplier = NULL; |
| } |
| Boolean integerOnly = 1; |
| switch (numberType) { |
| case kCFNumberSInt8Type: case kCFNumberCharType: |
| case kCFNumberSInt16Type: case kCFNumberShortType: |
| case kCFNumberSInt32Type: case kCFNumberIntType: |
| case kCFNumberLongType: case kCFNumberCFIndexType: |
| case kCFNumberSInt64Type: case kCFNumberLongLongType: |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 1); // ignored by ICU for rule-based formatters |
| break; |
| default: |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 0); // ignored by ICU for rule-based formatters |
| integerOnly = 0; |
| break; |
| } |
| int32_t dpos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| int64_t dreti = 0; |
| double dretd = 0.0; |
| if (isZero) { |
| dpos = rangep ? rangep->length : 0; |
| } else { |
| char buffer[1024]; |
| memset(buffer, 0, sizeof(buffer)); |
| int32_t len = __cficu_unum_parseDecimal(formatter->_nf, ustr, range.length, &dpos, buffer, sizeof(buffer), &status); |
| if (!U_FAILURE(status) && 0 < len && integerOnly) { |
| char *endptr = NULL; |
| errno = 0; |
| dreti = strtoll_l(buffer, &endptr, 10, NULL); |
| if (!(errno == 0 && *endptr == '\0')) status = U_INVALID_FORMAT_ERROR; |
| } |
| if (!U_FAILURE(status) && 0 < len) { |
| char *endptr = NULL; |
| errno = 0; |
| dretd = strtod_l(buffer, &endptr, NULL); |
| if (!(errno == 0 && *endptr == '\0')) status = U_INVALID_FORMAT_ERROR; |
| } |
| } |
| if (formatter->_isLenient) { |
| if (rangep) { |
| CFIndex uncompEnd = rangep->location + rangep->length; |
| CFIndex uncompIdx = rangep->location; |
| for (CFIndex compIdx = 0; compIdx < dpos && uncompIdx < uncompEnd; compIdx++, uncompIdx++) { |
| while (uncompIdx < uncompEnd && ustr[compIdx] != __CFNumberFormatterNormalizeCharacter(CFStringGetCharacterAtIndex(string, uncompIdx))) uncompIdx++; |
| } |
| rangep->length = uncompIdx - rangep->location; |
| } |
| __CFNumberFormatterApplyPattern(formatter, formatter->_format); |
| if (formatter->_multiplier) CFRelease(formatter->_multiplier); |
| formatter->_multiplier = NULL; |
| } else if (rangep) { |
| rangep->length = dpos + (range.location - rangep->location); |
| } |
| formatter->_multiplier = multiplierRef; |
| CFRelease(stringToParse); |
| if (U_FAILURE(status)) { |
| return false; |
| } |
| if (formatter->_multiplier) { |
| double multiplier = 1.0; |
| if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { |
| multiplier = 1.0; |
| } |
| dreti = (int64_t)((double)dreti / multiplier); // integer truncation, plus double cast can be lossy for dreti > 2^53 |
| dretd = dretd / multiplier; |
| } |
| switch (numberType) { |
| case kCFNumberSInt8Type: case kCFNumberCharType: |
| if (INT8_MIN <= dreti && dreti <= INT8_MAX) { |
| *(int8_t *)valuePtr = (int8_t)dreti; |
| return true; |
| } |
| break; |
| case kCFNumberSInt16Type: case kCFNumberShortType: |
| if (INT16_MIN <= dreti && dreti <= INT16_MAX) { |
| *(int16_t *)valuePtr = (int16_t)dreti; |
| return true; |
| } |
| break; |
| case kCFNumberSInt32Type: case kCFNumberIntType: |
| #if !__LP64__ |
| case kCFNumberLongType: case kCFNumberCFIndexType: |
| #endif |
| if (INT32_MIN <= dreti && dreti <= INT32_MAX) { |
| *(int32_t *)valuePtr = (int32_t)dreti; |
| return true; |
| } |
| break; |
| case kCFNumberSInt64Type: case kCFNumberLongLongType: |
| #if __LP64__ |
| case kCFNumberLongType: case kCFNumberCFIndexType: |
| #endif |
| if (INT64_MIN <= dreti && dreti <= INT64_MAX) { |
| *(int64_t *)valuePtr = (int64_t)dreti; |
| return true; |
| } |
| break; |
| case kCFNumberFloat32Type: case kCFNumberFloatType: |
| if (-FLT_MAX <= dretd && dretd <= FLT_MAX) { |
| *(float *)valuePtr = (float)dretd; |
| return true; |
| } |
| break; |
| case kCFNumberFloat64Type: case kCFNumberDoubleType: |
| if (-DBL_MAX <= dretd && dretd <= DBL_MAX) { |
| *(double *)valuePtr = (double)dretd; |
| return true; |
| } |
| break; |
| } |
| return false; |
| } |
| |
| void CFNumberFormatterSetProperty(CFNumberFormatterRef formatter, CFStringRef key, CFTypeRef value) { |
| int32_t n; |
| double d; |
| UErrorCode status = U_ZERO_ERROR; |
| UChar ubuffer[BUFFER_SIZE]; |
| CFIndex cnt; |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| __CFGenericValidateType(key, CFStringGetTypeID()); |
| // rule-based formatters don't do attributes and symbols, except for one |
| if (CFEqual(kCFNumberFormatterFormattingContextKey, key)) { |
| #if U_ICU_VERSION_MAJOR_NUM >= 55 |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setContext(formatter->_nf, n, &status); |
| #endif |
| } |
| if (kCFNumberFormatterSpellOutStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; |
| if (kCFNumberFormatterOrdinalStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; |
| if (kCFNumberFormatterDurationStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; |
| if (kCFNumberFormatterCurrencyPluralStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; |
| if (kCFNumberFormatterCurrencyCodeKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterDecimalSeparatorKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterCurrencyDecimalSeparatorKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterAlwaysShowDecimalSeparatorKey == key) { |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN, (kCFBooleanTrue == value)); |
| } else if (kCFNumberFormatterGroupingSeparatorKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, (const UChar *)ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterUseGroupingSeparatorKey == key) { |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_GROUPING_USED, (kCFBooleanTrue == value)); |
| } else if (kCFNumberFormatterPercentSymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterZeroSymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| CFStringRef old = formatter->_zeroSym; |
| formatter->_zeroSym = value ? (CFStringRef)CFRetain(value) : NULL; |
| if (old) CFRelease(old); |
| } else if (kCFNumberFormatterNaNSymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterInfinitySymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterMinusSignKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterPlusSignKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterCurrencySymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, (const UChar *)ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterExponentSymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterMinIntegerDigitsKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS, n); |
| } else if (kCFNumberFormatterMaxIntegerDigitsKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS, n); |
| } else if (kCFNumberFormatterMinFractionDigitsKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS, n); |
| } else if (kCFNumberFormatterMaxFractionDigitsKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS, n); |
| } else if (kCFNumberFormatterGroupingSizeKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_GROUPING_SIZE, n); |
| } else if (kCFNumberFormatterSecondaryGroupingSizeKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE, n); |
| } else if (kCFNumberFormatterRoundingModeKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_ROUNDING_MODE, n); |
| } else if (kCFNumberFormatterRoundingIncrementKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberDoubleType, &d); |
| __cficu_unum_setDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT, d); |
| } else if (kCFNumberFormatterFormatWidthKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_FORMAT_WIDTH, n); |
| } else if (kCFNumberFormatterPaddingPositionKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_PADDING_POSITION, n); |
| } else if (kCFNumberFormatterPaddingCharacterKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterDefaultFormatKey == key) { |
| // read-only attribute |
| } else if (kCFNumberFormatterMultiplierKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberRef old = formatter->_multiplier; |
| formatter->_multiplier = value ? (CFNumberRef)CFRetain(value) : NULL; |
| formatter->_userSetMultiplier = value ? true : false; |
| if (old) CFRelease(old); |
| } else if (kCFNumberFormatterPositivePrefixKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterPositiveSuffixKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterNegativePrefixKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterNegativeSuffixKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterPerMillSymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterInternationalCurrencySymbolKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterCurrencyGroupingSeparatorKey == key) { |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| cnt = CFStringGetLength((CFStringRef)value); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| __cficu_unum_setSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, cnt, &status); |
| } else if (kCFNumberFormatterIsLenientKey == key) { |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| formatter->_isLenient = (kCFBooleanTrue == value); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_LENIENT_PARSE, (kCFBooleanTrue == value)); |
| } else if (kCFNumberFormatterUseSignificantDigitsKey == key) { |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED, (kCFBooleanTrue == value)); |
| } else if (kCFNumberFormatterMinSignificantDigitsKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS, n); |
| } else if (kCFNumberFormatterMaxSignificantDigitsKey == key) { |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); |
| __cficu_unum_setAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS, n); |
| } else if (kCFNumberFormatterUsesCharacterDirectionKey == key) { |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| formatter->_usesCharacterDirection = value == kCFBooleanTrue; |
| } else { |
| CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); |
| } |
| if (_CFExecutableLinkedOnOrAfter(CFSystemVersionSnowLeopard)) { |
| // do a dummy call to CFNumberFormatterGetFormat() after changing an attribute because |
| // ICU sometimes changes the pattern due to a property change, and we need to poke |
| // __cficu_unum_toPattern() and also update our own variables |
| CFNumberFormatterGetFormat(formatter); |
| } |
| } |
| |
| CFTypeRef CFNumberFormatterCopyProperty(CFNumberFormatterRef formatter, CFStringRef key) { |
| int32_t n; |
| double d; |
| UErrorCode status = U_ZERO_ERROR; |
| UChar ubuffer[BUFFER_SIZE]; |
| CFIndex cnt; |
| __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); |
| __CFGenericValidateType(key, CFStringGetTypeID()); |
| // rule-based formatters don't do attributes and symbols, except for one |
| if (CFEqual(kCFNumberFormatterFormattingContextKey, key)) { |
| #if U_ICU_VERSION_MAJOR_NUM >= 55 |
| n = __cficu_unum_getContext(formatter->_nf, UDISPCTX_TYPE_CAPITALIZATION, &status); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| #endif |
| } |
| if (kCFNumberFormatterSpellOutStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; |
| if (kCFNumberFormatterOrdinalStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; |
| if (kCFNumberFormatterDurationStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; |
| if (kCFNumberFormatterCurrencyPluralStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; |
| if (kCFNumberFormatterCurrencyCodeKey == key) { |
| cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt == 0) { |
| CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); |
| char buffer[BUFFER_SIZE]; |
| const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); |
| if (NULL == cstr) { |
| if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; |
| } |
| if (NULL == cstr) { |
| return NULL; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| UNumberFormat *nf = __cficu_unum_open(UNUM_CURRENCY, NULL, 0, cstr, NULL, &status); |
| if (NULL != nf) { |
| cnt = __cficu_unum_getTextAttribute(nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status); |
| __cficu_unum_close(nf); |
| } |
| } |
| if (U_SUCCESS(status) && 0 < cnt && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterDecimalSeparatorKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterCurrencyDecimalSeparatorKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, (UChar *)ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterAlwaysShowDecimalSeparatorKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN); |
| if (1) { |
| return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); |
| } |
| } else if (kCFNumberFormatterGroupingSeparatorKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterUseGroupingSeparatorKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_GROUPING_USED); |
| if (1) { |
| return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); |
| } |
| } else if (kCFNumberFormatterPercentSymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterZeroSymbolKey == key) { |
| return formatter->_zeroSym ? CFRetain(formatter->_zeroSym) : NULL; |
| } else if (kCFNumberFormatterNaNSymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterInfinitySymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterMinusSignKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterPlusSignKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterCurrencySymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterExponentSymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterMinIntegerDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterMaxIntegerDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterMinFractionDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterMaxFractionDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterGroupingSizeKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_GROUPING_SIZE); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterSecondaryGroupingSizeKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterRoundingModeKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_ROUNDING_MODE); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterRoundingIncrementKey == key) { |
| d = __cficu_unum_getDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberDoubleType, &d); |
| } |
| } else if (kCFNumberFormatterFormatWidthKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_FORMAT_WIDTH); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterPaddingPositionKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_PADDING_POSITION); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterPaddingCharacterKey == key) { |
| cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterDefaultFormatKey == key) { |
| return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL; |
| } else if (kCFNumberFormatterMultiplierKey == key) { |
| return formatter->_multiplier ? CFRetain(formatter->_multiplier) : NULL; |
| } else if (kCFNumberFormatterPositivePrefixKey == key) { |
| cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterPositiveSuffixKey == key) { |
| cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterNegativePrefixKey == key) { |
| cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterNegativeSuffixKey == key) { |
| cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterPerMillSymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterInternationalCurrencySymbolKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterCurrencyGroupingSeparatorKey == key) { |
| cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); |
| } |
| } else if (kCFNumberFormatterIsLenientKey == key) { |
| // __cficu_unum_getAttribute(, UNUM_LENIENT_PARSE) is undefined. |
| return CFRetain(formatter->_isLenient ? kCFBooleanTrue : kCFBooleanFalse); |
| } else if (kCFNumberFormatterUseSignificantDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED); |
| if (1) { |
| return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); |
| } |
| } else if (kCFNumberFormatterMinSignificantDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else if (kCFNumberFormatterMaxSignificantDigitsKey == key) { |
| n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS); |
| if (1) { |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); |
| } |
| } else { |
| CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); |
| } |
| return NULL; |
| } |
| |
| |
| Boolean CFNumberFormatterGetDecimalInfoForCurrencyCode(CFStringRef currencyCode, int32_t *defaultFractionDigits, double *roundingIncrement) { |
| UChar ubuffer[4]; |
| __CFGenericValidateType(currencyCode, CFStringGetTypeID()); |
| CFAssert1(3 == CFStringGetLength(currencyCode), __kCFLogAssertion, "%s(): currencyCode is not 3 characters", __PRETTY_FUNCTION__); |
| CFStringGetCharacters(currencyCode, CFRangeMake(0, 3), (UniChar *)ubuffer); |
| ubuffer[3] = 0; |
| UErrorCode icuStatus = U_ZERO_ERROR; |
| if (defaultFractionDigits) *defaultFractionDigits = __cficu_ucurr_getDefaultFractionDigits(ubuffer, &icuStatus); |
| if (roundingIncrement) *roundingIncrement = __cficu_ucurr_getRoundingIncrement(ubuffer, &icuStatus); |
| if (U_FAILURE(icuStatus)) |
| return false; |
| return (!defaultFractionDigits || 0 <= *defaultFractionDigits) && (!roundingIncrement || 0.0 <= *roundingIncrement); |
| } |
| |
| // This is for NSNumberFormatter use only! |
| void *_CFNumberFormatterGetFormatter(CFNumberFormatterRef formatter) { |
| return (void *)formatter->_nf; |
| } |
| |
| #undef BUFFER_SIZE |
| |