1/*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*	CFNumberFormatter.c
25	Copyright (c) 2002-2013, Apple Inc. All rights reserved.
26	Responsibility: David Smith
27*/
28
29#include <CoreFoundation/CFNumberFormatter.h>
30#include <CoreFoundation/ForFoundationOnly.h>
31#include <CoreFoundation/CFBigNumber.h>
32#include "CFInternal.h"
33#include "CFLocaleInternal.h"
34#include "CFICULogging.h"
35#include <math.h>
36#include <float.h>
37
38
39static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter);
40static CFStringRef __CFNumberFormatterCreateCompressedString(CFStringRef inString, Boolean isFormat, CFRange *rangep);
41static UErrorCode __CFNumberFormatterApplyPattern(CFNumberFormatterRef formatter, CFStringRef pattern);
42
43#define BUFFER_SIZE 768
44
45struct __CFNumberFormatter {
46    CFRuntimeBase _base;
47    UNumberFormat *_nf;
48    CFLocaleRef _locale;
49    CFNumberFormatterStyle _style;
50    CFStringRef _format;	// NULL for RBNFs
51    CFStringRef _defformat;
52    CFStringRef _compformat;
53    CFNumberRef _multiplier;
54    CFStringRef _zeroSym;
55    Boolean _isLenient;
56    Boolean _userSetMultiplier;
57    Boolean _usesCharacterDirection;
58};
59
60static CFStringRef __CFNumberFormatterCopyDescription(CFTypeRef cf) {
61    CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf;
62    return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFNumberFormatter %p [%p]>"), cf, CFGetAllocator(formatter));
63}
64
65static void __CFNumberFormatterDeallocate(CFTypeRef cf) {
66    CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf;
67    if (formatter->_nf) __cficu_unum_close(formatter->_nf);
68    if (formatter->_locale) CFRelease(formatter->_locale);
69    if (formatter->_format) CFRelease(formatter->_format);
70    if (formatter->_defformat) CFRelease(formatter->_defformat);
71    if (formatter->_compformat) CFRelease(formatter->_compformat);
72    if (formatter->_multiplier) CFRelease(formatter->_multiplier);
73    if (formatter->_zeroSym) CFRelease(formatter->_zeroSym);
74}
75
76static CFTypeID __kCFNumberFormatterTypeID = _kCFRuntimeNotATypeID;
77
78static const CFRuntimeClass __CFNumberFormatterClass = {
79    0,
80    "CFNumberFormatter",
81    NULL,	// init
82    NULL,	// copy
83    __CFNumberFormatterDeallocate,
84    NULL,
85    NULL,
86    NULL,	//
87    __CFNumberFormatterCopyDescription
88};
89
90static void __CFNumberFormatterInitialize(void) {
91    __kCFNumberFormatterTypeID = _CFRuntimeRegisterClass(&__CFNumberFormatterClass);
92}
93
94CFTypeID CFNumberFormatterGetTypeID(void) {
95    if (_kCFRuntimeNotATypeID == __kCFNumberFormatterTypeID) __CFNumberFormatterInitialize();
96    return __kCFNumberFormatterTypeID;
97}
98
99CFNumberFormatterRef CFNumberFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFNumberFormatterStyle style) {
100    struct __CFNumberFormatter *memory;
101    uint32_t size = sizeof(struct __CFNumberFormatter) - sizeof(CFRuntimeBase);
102    if (allocator == NULL) allocator = __CFGetDefaultAllocator();
103    __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
104    __CFGenericValidateType(locale, CFLocaleGetTypeID());
105    memory = (struct __CFNumberFormatter *)_CFRuntimeCreateInstance(allocator, CFNumberFormatterGetTypeID(), size, NULL);
106    if (NULL == memory) {
107	return NULL;
108    }
109    memory->_nf = NULL;
110    memory->_locale = NULL;
111    memory->_format = NULL;
112    memory->_defformat = NULL;
113    memory->_compformat = NULL;
114    memory->_multiplier = NULL;
115    memory->_zeroSym = NULL;
116    memory->_isLenient = false;
117    memory->_userSetMultiplier = false;
118    memory->_usesCharacterDirection = false;
119    if (NULL == locale) locale = CFLocaleGetSystem();
120    memory->_style = style;
121    uint32_t ustyle;
122    switch (style) {
123    case kCFNumberFormatterNoStyle: ustyle = UNUM_IGNORE; break;
124    case kCFNumberFormatterDecimalStyle: ustyle = UNUM_DECIMAL; break;
125    case kCFNumberFormatterCurrencyStyle: ustyle = UNUM_CURRENCY; break;
126    case kCFNumberFormatterPercentStyle: ustyle = UNUM_PERCENT; break;
127    case kCFNumberFormatterScientificStyle: ustyle = UNUM_SCIENTIFIC; break;
128    case kCFNumberFormatterSpellOutStyle: ustyle = UNUM_SPELLOUT; break;
129    case kCFNumberFormatterOrdinalStyle: ustyle = UNUM_ORDINAL; break;
130    case kCFNumberFormatterDurationStyle: ustyle = UNUM_DURATION; break;
131    default:
132	CFAssert2(0, __kCFLogAssertion, "%s(): unknown style %d", __PRETTY_FUNCTION__, style);
133	ustyle = UNUM_DECIMAL;
134	memory->_style = kCFNumberFormatterDecimalStyle;
135	break;
136    }
137    CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR("");
138    char buffer[BUFFER_SIZE];
139    const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
140    if (NULL == cstr) {
141	if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
142    }
143    if (NULL == cstr) {
144	CFRelease(memory);
145	return NULL;
146    }
147    UErrorCode status = U_ZERO_ERROR;
148    memory->_nf = __cficu_unum_open((UNumberFormatStyle)ustyle, NULL, 0, cstr, NULL, &status);
149    CFAssert2(memory->_nf, __kCFLogAssertion, "%s(): error (%d) creating number formatter", __PRETTY_FUNCTION__, status);
150    if (NULL == memory->_nf) {
151	CFRelease(memory);
152	return NULL;
153    }
154    UChar ubuff[4];
155    if (kCFNumberFormatterNoStyle == style) {
156        status = U_ZERO_ERROR;
157	ubuff[0] = '#'; ubuff[1] = ';'; ubuff[2] = '#';
158        __cficu_unum_applyPattern(memory->_nf, false, ubuff, 3, NULL, &status);
159	__cficu_unum_setAttribute(memory->_nf, UNUM_MAX_INTEGER_DIGITS, 42);
160	__cficu_unum_setAttribute(memory->_nf, UNUM_MAX_FRACTION_DIGITS, 0);
161    }
162    memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : CFLocaleGetSystem();
163    __CFNumberFormatterCustomize(memory);
164    if (kCFNumberFormatterSpellOutStyle != memory->_style && kCFNumberFormatterOrdinalStyle != memory->_style && kCFNumberFormatterDurationStyle != memory->_style) {
165	UChar ubuffer[BUFFER_SIZE];
166	status = U_ZERO_ERROR;
167	int32_t ret = __cficu_unum_toPattern(memory->_nf, false, ubuffer, BUFFER_SIZE, &status);
168	if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
169	    memory->_format = CFStringCreateWithCharacters(allocator, (const UniChar *)ubuffer, ret);
170	}
171    }
172    memory->_defformat = memory->_format ? (CFStringRef)CFRetain(memory->_format) : NULL;
173    memory->_compformat = memory->_format ? __CFNumberFormatterCreateCompressedString(memory->_format, true, NULL) : NULL;
174    if (kCFNumberFormatterSpellOutStyle != memory->_style && kCFNumberFormatterOrdinalStyle != memory->_style && kCFNumberFormatterDurationStyle != memory->_style) {
175	int32_t n = __cficu_unum_getAttribute(memory->_nf, UNUM_MULTIPLIER);
176	if (1 != n) {
177	    memory->_multiplier = CFNumberCreate(allocator, kCFNumberSInt32Type, &n);
178	    __cficu_unum_setAttribute(memory->_nf, UNUM_MULTIPLIER, 1);
179	}
180    }
181    __cficu_unum_setAttribute(memory->_nf, UNUM_LENIENT_PARSE, 0);
182    return (CFNumberFormatterRef)memory;
183}
184
185extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale);
186
187static void __substituteFormatStringFromPrefsNF(CFNumberFormatterRef formatter) {
188    CFIndex formatStyle = formatter->_style;
189    if (kCFNumberFormatterSpellOutStyle == formatStyle) return;
190    if (kCFNumberFormatterOrdinalStyle == formatStyle) return;
191    if (kCFNumberFormatterDurationStyle == formatStyle) return;
192    CFStringRef prefName = CFSTR("AppleICUNumberFormatStrings");
193    if (kCFNumberFormatterNoStyle != formatStyle) {
194	CFStringRef pref = NULL;
195	CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale);
196	CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, prefName) : NULL;
197	if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
198	    CFStringRef key;
199	    switch (formatStyle) {
200	    case kCFNumberFormatterDecimalStyle: key = CFSTR("1"); break;
201	    case kCFNumberFormatterCurrencyStyle: key = CFSTR("2"); break;
202	    case kCFNumberFormatterPercentStyle: key = CFSTR("3"); break;
203	    case kCFNumberFormatterScientificStyle: key = CFSTR("4"); break;
204	    case kCFNumberFormatterSpellOutStyle: key = CFSTR("5"); break;
205	    case kCFNumberFormatterOrdinalStyle: key = CFSTR("6"); break;
206	    case kCFNumberFormatterDurationStyle: key = CFSTR("7"); break;
207	    default: key = CFSTR("0"); break;
208	    }
209	    pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key);
210	}
211	if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) {
212	    int32_t icustyle = UNUM_IGNORE;
213	    switch (formatStyle) {
214	    case kCFNumberFormatterDecimalStyle: icustyle = UNUM_DECIMAL; break;
215	    case kCFNumberFormatterCurrencyStyle: icustyle = UNUM_CURRENCY; break;
216	    case kCFNumberFormatterPercentStyle: icustyle = UNUM_PERCENT; break;
217	    case kCFNumberFormatterScientificStyle: icustyle = UNUM_SCIENTIFIC; break;
218	    case kCFNumberFormatterSpellOutStyle: icustyle = UNUM_SPELLOUT; break;
219	    case kCFNumberFormatterOrdinalStyle: icustyle = UNUM_ORDINAL; break;
220	    case kCFNumberFormatterDurationStyle: icustyle = UNUM_DURATION; break;
221	    }
222	    CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
223	    char buffer[BUFFER_SIZE];
224	    const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
225	    if (NULL == cstr) {
226		if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
227	    }
228	    UErrorCode status = U_ZERO_ERROR;
229	    UNumberFormat *nf = __cficu_unum_open((UNumberFormatStyle)icustyle, NULL, 0, cstr, NULL, &status);
230	    if (NULL != nf) {
231		UChar ubuffer[BUFFER_SIZE];
232		status = U_ZERO_ERROR;
233		int32_t number_len = __cficu_unum_toPattern(nf, false, ubuffer, BUFFER_SIZE, &status);
234		if (U_SUCCESS(status) && number_len <= BUFFER_SIZE) {
235		    CFStringRef numberString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, number_len);
236		    status = U_ZERO_ERROR;
237		    int32_t formatter_len = __cficu_unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status);
238		    if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) {
239			CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
240			CFStringAppendCharacters(formatString, (const UniChar *)ubuffer, formatter_len);
241			// find numberString inside formatString, substitute the pref in that range
242			CFRange result;
243			if (CFStringFindWithOptions(formatString, numberString, CFRangeMake(0, formatter_len), 0, &result)) {
244			    CFStringReplace(formatString, result, pref);
245			    __CFNumberFormatterApplyPattern(formatter, formatString);
246			}
247			CFRelease(formatString);
248		    }
249		    CFRelease(numberString);
250		}
251		__cficu_unum_close(nf);
252	    }
253	}
254    }
255}
256
257static UniChar __CFNumberFormatterNormalizeCharacter(UniChar c) {
258    if (CFCharacterSetIsCharacterMember(CFCharacterSetGetPredefined(kCFCharacterSetWhitespace), c)) {
259	return ' ';
260    } else {
261	return c;
262    }
263}
264
265/* 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. */
266static CFStringRef __CFNumberFormatterCreateCompressedString(CFStringRef inString, Boolean isFormat, CFRange *rangep) {
267    if (!inString) return NULL;
268    CFRange range = { 0, 0 };
269    if (rangep) {
270	range = *rangep;
271    } else {
272	range.length = CFStringGetLength(inString);
273    }
274    CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
275    CFCharacterSetRef letters = CFCharacterSetGetPredefined(kCFCharacterSetLetter);
276    CFCharacterSetRef numbers = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
277    UniChar prevCh = 0, nextCh = 0;
278    Boolean inQuote = false;
279    for (CFIndex in_idx = range.location; in_idx < range.location + range.length; in_idx++) {
280        UniChar ch = __CFNumberFormatterNormalizeCharacter(CFStringGetCharacterAtIndex(inString, in_idx));
281	nextCh = (in_idx+1 < range.length) ? CFStringGetCharacterAtIndex(inString, in_idx+1) : 0;
282	if (isFormat && ch == '\'') inQuote = !inQuote;
283	if (inQuote || ch != ' ' || (CFCharacterSetIsCharacterMember(letters, prevCh) && CFCharacterSetIsCharacterMember(letters, nextCh)) || (CFCharacterSetIsCharacterMember(numbers, prevCh) && CFCharacterSetIsCharacterMember(numbers, nextCh))) {
284	    CFStringAppendCharacters(outString, &ch, 1);
285	    prevCh = ch;
286	}
287    }
288    return outString;
289}
290
291// Should not be called for rule-based ICU formatters; not supported
292static void __CFNumberFormatterApplySymbolPrefs(const void *key, const void *value, void *context) {
293    if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFStringGetTypeID()) {
294	CFNumberFormatterRef formatter = (CFNumberFormatterRef)context;
295	UNumberFormatSymbol sym = (UNumberFormatSymbol)CFStringGetIntValue((CFStringRef)key);
296	CFStringRef item = (CFStringRef)value;
297	CFIndex item_cnt = CFStringGetLength(item);
298	STACK_BUFFER_DECL(UChar, item_buffer, item_cnt);
299	UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item);
300	if (NULL == item_ustr) {
301	    CFStringGetCharacters(item, CFRangeMake(0, __CFMin(BUFFER_SIZE, item_cnt)), (UniChar *)item_buffer);
302	    item_ustr = item_buffer;
303	}
304	UErrorCode status = U_ZERO_ERROR;
305	__cficu_unum_setSymbol(formatter->_nf, sym, item_ustr, item_cnt, &status);
306   }
307}
308
309// Should not be called for rule-based ICU formatters
310static UErrorCode __CFNumberFormatterApplyPattern(CFNumberFormatterRef formatter, CFStringRef pattern) {
311    if (kCFNumberFormatterSpellOutStyle == formatter->_style) return U_UNSUPPORTED_ERROR;
312    if (kCFNumberFormatterOrdinalStyle == formatter->_style) return U_UNSUPPORTED_ERROR;
313    if (kCFNumberFormatterDurationStyle == formatter->_style) return U_UNSUPPORTED_ERROR;
314    CFIndex cnt = CFStringGetLength(pattern);
315    STACK_BUFFER_DECL(UChar, ubuffer, cnt);
316    const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(pattern);
317    if (NULL == ustr) {
318	CFStringGetCharacters(pattern, CFRangeMake(0, cnt), (UniChar *)ubuffer);
319	ustr = ubuffer;
320    }
321    UErrorCode status = U_ZERO_ERROR;
322    __cficu_unum_applyPattern(formatter->_nf, false, ustr, cnt, NULL, &status);
323
324    // __cficu_unum_applyPattern() may have magically changed other attributes based on
325    // the contents of the format string; we simply expose that ICU behavior, except
326    // for UNUM_MULTIPLIER, which we re-read and reset, like we did at initialization
327    // time though any user-set multiplier state takes precedence.
328    if (formatter->_userSetMultiplier) {
329	__cficu_unum_setAttribute(formatter->_nf, UNUM_MULTIPLIER, 1);
330    } else {
331	if (formatter->_multiplier) CFRelease(formatter->_multiplier);
332        formatter->_multiplier = NULL;
333        int32_t n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MULTIPLIER);
334        if (1 != n) {
335	    formatter->_multiplier = CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
336	    __cficu_unum_setAttribute(formatter->_nf, UNUM_MULTIPLIER, 1);
337        }
338    }
339    return status;
340}
341
342static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter) {
343    __substituteFormatStringFromPrefsNF(formatter);
344    CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale);
345    CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUNumberSymbols")) : NULL;
346    if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
347	CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFNumberFormatterApplySymbolPrefs, formatter);
348    }
349}
350
351CFLocaleRef CFNumberFormatterGetLocale(CFNumberFormatterRef formatter) {
352    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
353    return formatter->_locale;
354}
355
356CFNumberFormatterStyle CFNumberFormatterGetStyle(CFNumberFormatterRef formatter) {
357    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
358    return formatter->_style;
359}
360
361CFStringRef CFNumberFormatterGetFormat(CFNumberFormatterRef formatter) {
362    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
363    if (kCFNumberFormatterSpellOutStyle == formatter->_style) return NULL;
364    if (kCFNumberFormatterOrdinalStyle == formatter->_style) return NULL;
365    if (kCFNumberFormatterDurationStyle == formatter->_style) return NULL;
366    UChar ubuffer[BUFFER_SIZE];
367    CFStringRef newString = NULL;
368    UErrorCode status = U_ZERO_ERROR;
369    int32_t ret = __cficu_unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status);
370    if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
371	newString = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ret);
372    }
373    if (newString && !formatter->_format) {
374	formatter->_format = newString;
375	if (formatter->_compformat) CFRelease(formatter->_compformat);
376	formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL);
377    } else if (newString && !CFEqual(newString, formatter->_format)) {
378	CFRelease(formatter->_format);
379	formatter->_format = newString;
380	if (formatter->_compformat) CFRelease(formatter->_compformat);
381	formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL);
382    } else if (newString) {
383	CFRelease(newString);
384    }
385    return formatter->_format;
386}
387
388void CFNumberFormatterSetFormat(CFNumberFormatterRef formatter, CFStringRef formatString) {
389    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
390    __CFGenericValidateType(formatString, CFStringGetTypeID());
391    if (kCFNumberFormatterSpellOutStyle == formatter->_style) return;
392    if (kCFNumberFormatterOrdinalStyle == formatter->_style) return;
393    if (kCFNumberFormatterDurationStyle == formatter->_style) return;
394    CFIndex cnt = CFStringGetLength(formatString);
395    CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__);
396    if ((!formatter->_format || !CFEqual(formatter->_format, formatString)) && cnt <= 1024) {
397	UErrorCode status = __CFNumberFormatterApplyPattern(formatter, formatString);
398	if (U_SUCCESS(status)) {
399	    UChar ubuffer2[BUFFER_SIZE];
400	    status = U_ZERO_ERROR;
401	    int32_t ret = __cficu_unum_toPattern(formatter->_nf, false, ubuffer2, BUFFER_SIZE, &status);
402	    if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
403	        if (formatter->_format) CFRelease(formatter->_format);
404		formatter->_format = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer2, ret);
405		if (formatter->_compformat) CFRelease(formatter->_compformat);
406		formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL);
407	    }
408	}
409    }
410}
411
412#define GET_MULTIPLIER                      \
413        double multiplier = 1.0;            \
414        double dummy = 0.0;                 \
415        if (formatter->_multiplier) {       \
416            if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { \
417                multiplier = 1.0;           \
418            }                               \
419        }                                   \
420        if (modf(multiplier, &dummy) < FLT_EPSILON) {   \
421            multiplier = floor(multiplier);             \
422        }
423
424CFStringRef CFNumberFormatterCreateStringWithNumber(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberRef number) {
425    if (allocator == NULL) allocator = __CFGetDefaultAllocator();
426    __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
427    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
428    __CFGenericValidateType(number, CFNumberGetTypeID());
429    // The values of CFNumbers with large unsigned 64-bit ints don't survive well through this
430    CFNumberType type = CFNumberGetType(number);
431    char buffer[64];
432    CFNumberGetValue(number, type, buffer);
433    return CFNumberFormatterCreateStringWithValue(allocator, formatter, type, buffer);
434}
435
436#define FORMAT_FLT(T, FUNC)							\
437	T value = *(T *)valuePtr;					\
438	if (0 == value && formatter->_zeroSym) { return (CFStringRef)CFRetain(formatter->_zeroSym); }	\
439	if (1.0 != multiplier) {					\
440		value = (T)(value * multiplier);                     \
441	}								\
442	status = U_ZERO_ERROR;						\
443	used = FUNC(formatter->_nf, value, ubuffer + 1, cnt, NULL, &status); \
444	if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) {		\
445	    cnt = used + 1 + 1;						\
446	    ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); \
447	    status = U_ZERO_ERROR;					\
448	    used = FUNC(formatter->_nf, value, ustr + 1, cnt, NULL, &status); \
449	}
450
451#define FORMAT_INT(T, FUN)                                                   \
452        T value = *(T *)valuePtr;					\
453        if (0 == value && formatter->_zeroSym) { return (CFStringRef)CFRetain(formatter->_zeroSym); }	\
454        if (1.0 != multiplier) {					\
455            value = (T)(value * multiplier);                        \
456        }                                                           \
457        _CFBigNum bignum;                                           \
458        FUN(&bignum, value);                                        \
459        char buffer[BUFFER_SIZE + 1];                                           \
460        _CFBigNumToCString(&bignum, false, true, buffer, BUFFER_SIZE);      \
461        status = U_ZERO_ERROR;                                      \
462        used = __cficu_unum_formatDecimal(formatter->_nf, buffer, strlen(buffer), ubuffer + 1, BUFFER_SIZE, NULL, &status);     \
463        if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) {      \
464            cnt = used + 1 + 1;                                         \
465            ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0);                 \
466            status = U_ZERO_ERROR;                                  \
467            used = __cficu_unum_formatDecimal(formatter->_nf, buffer, strlen(buffer), ustr + 1, cnt, NULL, &status);            \
468        }                                                           \
469
470CFStringRef CFNumberFormatterCreateStringWithValue(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberType numberType, const void *valuePtr) {
471    if (allocator == NULL) allocator = __CFGetDefaultAllocator();
472    __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
473    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
474    GET_MULTIPLIER;
475    UChar *ustr = NULL, ubuffer[BUFFER_SIZE + 1];
476    UErrorCode status = U_ZERO_ERROR;
477    CFIndex used, cnt = BUFFER_SIZE;
478    if (numberType == kCFNumberFloat64Type || numberType == kCFNumberDoubleType) {
479	FORMAT_FLT(double, __cficu_unum_formatDouble)
480    } else if (numberType == kCFNumberFloat32Type || numberType == kCFNumberFloatType) {
481	FORMAT_FLT(float, __cficu_unum_formatDouble)
482    } else if (numberType == kCFNumberSInt64Type || numberType == kCFNumberLongLongType) {
483	FORMAT_INT(int64_t, _CFBigNumInitWithInt64)
484    } else if (numberType == kCFNumberLongType || numberType == kCFNumberCFIndexType) {
485#if __LP64__
486	FORMAT_INT(int64_t, _CFBigNumInitWithInt64)
487#else
488	FORMAT_INT(int32_t, _CFBigNumInitWithInt32)
489#endif
490    } else if (numberType == kCFNumberSInt32Type || numberType == kCFNumberIntType) {
491	FORMAT_INT(int32_t, _CFBigNumInitWithInt32)
492    } else if (numberType == kCFNumberSInt16Type || numberType == kCFNumberShortType) {
493	FORMAT_INT(int16_t, _CFBigNumInitWithInt16)
494    } else if (numberType == kCFNumberSInt8Type || numberType == kCFNumberCharType) {
495	FORMAT_INT(int8_t, _CFBigNumInitWithInt8)
496    } else {
497	CFAssert2(0, __kCFLogAssertion, "%s(): unknown CFNumberType (%d)", __PRETTY_FUNCTION__, numberType);
498	return NULL;
499    }
500    CFStringRef string = NULL;
501    if (U_SUCCESS(status)) {
502        UniChar *bufferToUse = ustr ? (UniChar *)ustr : (UniChar *)ubuffer;
503        if (formatter->_usesCharacterDirection && CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter->_locale)) == kCFLocaleLanguageDirectionRightToLeft) {
504            // Insert Unicode RTL marker
505            bufferToUse[0] = 0x200F;
506            used++;
507        } else {
508            // Move past direction marker
509            bufferToUse++;
510        }
511        string = CFStringCreateWithCharacters(allocator, bufferToUse, used);
512    }
513    if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr);
514    return string;
515}
516
517#undef FORMAT_FLT
518#undef FORMAT_INT
519#undef GET_MULTIPLIER
520
521CFNumberRef CFNumberFormatterCreateNumberFromString(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFOptionFlags options) {
522    if (allocator == NULL) allocator = __CFGetDefaultAllocator();
523    __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
524    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
525    __CFGenericValidateType(string, CFStringGetTypeID());
526    char buffer[16] __attribute__ ((aligned (8)));
527    CFRange r = rangep ? *rangep : CFRangeMake(0, CFStringGetLength(string));
528    CFNumberRef multiplierRef = formatter->_multiplier;
529    formatter->_multiplier = NULL;
530    Boolean b = CFNumberFormatterGetValueFromString(formatter, string, &r, kCFNumberSInt64Type, buffer);
531    formatter->_multiplier = multiplierRef;
532    if (b) {
533        Boolean passedMultiplier = true;
534        // We handle the multiplier case here manually; the final
535        // result is supposed to be (parsed value) / (multiplier), but
536        // the int case here should only succeed if the parsed value
537        // is an exact multiple of the multiplier.
538        if (multiplierRef) {
539            int64_t tmp = *(int64_t *)buffer;
540            double multiplier = 1.0;
541            if (!CFNumberGetValue(multiplierRef, kCFNumberFloat64Type, &multiplier)) {
542                multiplier = 1.0;
543            }
544            double dummy;
545            if (llabs(tmp) < fabs(multiplier)) {
546                passedMultiplier = false;
547            } else if (fabs(multiplier) < 1.0) { // We can't handle this math yet
548                passedMultiplier = false;
549            } else if (modf(multiplier, &dummy) == 0.0) { // multiplier is an integer
550                int64_t imult = (int64_t)multiplier;
551                int64_t rem = tmp % imult;
552                if (rem != 0) passedMultiplier = false;
553                if (passedMultiplier) {
554                    tmp = tmp / imult;
555                    *(int64_t *)buffer = tmp;
556                }
557            } else if (multiplier == -1.0) { // simple
558                tmp = tmp * -1;
559                *(int64_t *)buffer = tmp;
560            } else if (multiplier != 1.0) {
561                // First, throw away integer multiples of the multiplier to
562                // bring the value down to less than 2^53, so that we can
563                // cast it to double without losing any precision, important
564                // for the "remainder is zero" test.
565                // Find power of two which, when multiplier is multiplied by it,
566                // results in an integer value. pwr will be <= 52 since multiplier
567                // is at least 1.
568                int pwr = 0;
569                double intgrl;
570                while (modf(scalbn(multiplier, pwr), &intgrl) != 0.0) pwr++;
571                int64_t i2 = (int64_t)intgrl;
572                // scale pwr and i2 up to a reasonably large value so the next loop doesn't take forever
573                while (llabs(i2) < (1LL << 50)) { i2 *= 2; pwr++; }
574                int64_t cnt = 0;
575                while ((1LL << 53) <= llabs(tmp)) {
576		    // subtract (multiplier * 2^pwr) each time
577                    tmp -= i2; // move tmp toward zero
578                    cnt += (1LL << pwr); // remember how many 2^pwr we subtracted
579                }
580                // If the integer is less than 2^53, there is no loss
581                // in converting it to double, so we can just do the
582                // direct operation now.
583                double rem = fmod((double)tmp, multiplier);
584                if (rem != 0.0) passedMultiplier = false;
585                if (passedMultiplier) {
586		    // The original tmp, which we need to divide by multiplier, is at this point:
587                    //   tmp + k * 2^n * multiplier, where k is the number of loop iterations
588                    // That original value needs to be divided by multiplier and put back in the
589                    // buffer.  Noting that k * 2^n == cnt, and after some algebra, we have:
590                    tmp = (int64_t)((double)tmp / multiplier) + cnt;
591                    *(int64_t *)buffer = tmp;
592                }
593            }
594        }
595        if (passedMultiplier && ((r.length == CFStringGetLength(string)) || (options & kCFNumberFormatterParseIntegersOnly))) {
596            if (rangep) *rangep = r;
597	    return CFNumberCreate(allocator, kCFNumberSInt64Type, buffer);
598        }
599    }
600    if (options & kCFNumberFormatterParseIntegersOnly) return NULL;
601    if (CFNumberFormatterGetValueFromString(formatter, string, rangep, kCFNumberFloat64Type, buffer)) {
602	return CFNumberCreate(allocator, kCFNumberFloat64Type, buffer);
603    }
604    return NULL;
605}
606
607Boolean CFNumberFormatterGetValueFromString(CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFNumberType numberType, void *valuePtr) {
608    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
609    __CFGenericValidateType(string, CFStringGetTypeID());
610    CFStringRef stringToParse = formatter->_isLenient ? __CFNumberFormatterCreateCompressedString(string, false, rangep) : (CFStringRef)CFRetain(string);
611    CFRange range = {0, 0};
612    if(formatter->_isLenient) {
613        range.length = CFStringGetLength(stringToParse);
614    } else {
615        if (rangep) {
616            range = *rangep;
617        } else {
618            range.length = CFStringGetLength(stringToParse);
619        }
620        // __cficu_unum_parse chokes on leading whitespace
621        CFCharacterSetRef whitespace = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace);
622        while(range.length > 0 && CFCharacterSetIsCharacterMember(whitespace, CFStringGetCharacterAtIndex(stringToParse, range.location))) {
623            range.location++;
624            range.length--;
625        }
626    }
627    Boolean isZero = false;
628    if (formatter->_zeroSym) {
629	CFStringRef zeroSym = formatter->_isLenient ? __CFNumberFormatterCreateCompressedString(formatter->_zeroSym, false, NULL) : (CFStringRef)CFRetain(formatter->_zeroSym);
630	if (kCFCompareEqualTo == CFStringCompare(stringToParse, zeroSym, 0)) {
631	    isZero = true;
632	}
633	CFRelease(zeroSym);
634    }
635    if (1024 < range.length) range.length = 1024;
636    const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(stringToParse);
637    STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1);
638    if (NULL == ustr) {
639	CFStringGetCharacters(stringToParse, range, (UniChar *)ubuffer);
640	ustr = ubuffer;
641    } else if (!formatter->_isLenient) {
642	ustr += range.location;
643    }
644    CFNumberRef multiplierRef = formatter->_multiplier;
645    formatter->_multiplier = NULL;
646    if (formatter->_isLenient) {
647        __CFNumberFormatterApplyPattern(formatter, formatter->_compformat);
648        if (formatter->_multiplier) CFRelease(formatter->_multiplier);
649        formatter->_multiplier = NULL;
650    }
651    Boolean integerOnly = 1;
652    switch (numberType) {
653    case kCFNumberSInt8Type: case kCFNumberCharType:
654    case kCFNumberSInt16Type: case kCFNumberShortType:
655    case kCFNumberSInt32Type: case kCFNumberIntType:
656    case kCFNumberLongType: case kCFNumberCFIndexType:
657    case kCFNumberSInt64Type: case kCFNumberLongLongType:
658	__cficu_unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 1); // ignored by ICU for rule-based formatters
659	break;
660    default:
661	__cficu_unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 0); // ignored by ICU for rule-based formatters
662	integerOnly = 0;
663	break;
664    }
665    int32_t dpos = 0;
666    UErrorCode status = U_ZERO_ERROR;
667    int64_t dreti = 0;
668    double dretd = 0.0;
669    if (isZero) {
670	dpos = rangep ? rangep->length : 0;
671    } else {
672	char buffer[1024];
673        memset(buffer, 0, sizeof(buffer));
674	int32_t len = __cficu_unum_parseDecimal(formatter->_nf, ustr, range.length, &dpos, buffer, sizeof(buffer), &status);
675        if (!U_FAILURE(status) && 0 < len && integerOnly) {
676	    char *endptr = NULL;
677	    errno = 0;
678	    dreti = strtoll_l(buffer, &endptr, 10, NULL);
679	    if (!(errno == 0 && *endptr == '\0')) status = U_INVALID_FORMAT_ERROR;
680	}
681	if (!U_FAILURE(status) && 0 < len) {
682	    char *endptr = NULL;
683	    errno = 0;
684	    dretd = strtod_l(buffer, &endptr, NULL);
685	    if (!(errno == 0 && *endptr == '\0')) status = U_INVALID_FORMAT_ERROR;
686	}
687    }
688    if (formatter->_isLenient) {
689	if (rangep) {
690	    CFIndex uncompEnd = rangep->location + rangep->length;
691	    CFIndex uncompIdx = rangep->location;
692	    for (CFIndex compIdx = 0; compIdx < dpos && uncompIdx < uncompEnd; compIdx++, uncompIdx++) {
693		while (uncompIdx < uncompEnd && ustr[compIdx] != __CFNumberFormatterNormalizeCharacter(CFStringGetCharacterAtIndex(string, uncompIdx))) uncompIdx++;
694	    }
695	    rangep->length = uncompIdx - rangep->location;
696	}
697	__CFNumberFormatterApplyPattern(formatter, formatter->_format);
698        if (formatter->_multiplier) CFRelease(formatter->_multiplier);
699        formatter->_multiplier = NULL;
700    } else if (rangep) {
701        rangep->length = dpos + (range.location - rangep->location);
702    }
703    formatter->_multiplier = multiplierRef;
704    CFRelease(stringToParse);
705    if (U_FAILURE(status)) {
706	return false;
707    }
708    if (formatter->_multiplier) {
709        double multiplier = 1.0;
710        if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) {
711            multiplier = 1.0;
712        }
713        dreti = (int64_t)((double)dreti / multiplier); // integer truncation, plus double cast can be lossy for dreti > 2^53
714        dretd = dretd / multiplier;
715    }
716    switch (numberType) {
717    case kCFNumberSInt8Type: case kCFNumberCharType:
718	if (INT8_MIN <= dreti && dreti <= INT8_MAX) {
719	    *(int8_t *)valuePtr = (int8_t)dreti;
720	    return true;
721	}
722	break;
723    case kCFNumberSInt16Type: case kCFNumberShortType:
724	if (INT16_MIN <= dreti && dreti <= INT16_MAX) {
725	    *(int16_t *)valuePtr = (int16_t)dreti;
726	    return true;
727	}
728	break;
729    case kCFNumberSInt32Type: case kCFNumberIntType:
730#if !__LP64__
731    case kCFNumberLongType: case kCFNumberCFIndexType:
732#endif
733	if (INT32_MIN <= dreti && dreti <= INT32_MAX) {
734	    *(int32_t *)valuePtr = (int32_t)dreti;
735	    return true;
736	}
737	break;
738    case kCFNumberSInt64Type: case kCFNumberLongLongType:
739#if __LP64__
740    case kCFNumberLongType: case kCFNumberCFIndexType:
741#endif
742	if (INT64_MIN <= dreti && dreti <= INT64_MAX) {
743	    *(int64_t *)valuePtr = (int64_t)dreti;
744	    return true;
745	}
746	break;
747    case kCFNumberFloat32Type: case kCFNumberFloatType:
748	if (-FLT_MAX <= dretd && dretd <= FLT_MAX) {
749	    *(float *)valuePtr = (float)dretd;
750	    return true;
751	}
752	break;
753    case kCFNumberFloat64Type: case kCFNumberDoubleType:
754	if (-DBL_MAX <= dretd && dretd <= DBL_MAX) {
755	    *(double *)valuePtr = (double)dretd;
756	    return true;
757	}
758	break;
759    }
760    return false;
761}
762
763void CFNumberFormatterSetProperty(CFNumberFormatterRef formatter, CFStringRef key, CFTypeRef value) {
764    int32_t n;
765    double d;
766    UErrorCode status = U_ZERO_ERROR;
767    UChar ubuffer[BUFFER_SIZE];
768    CFIndex cnt;
769    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
770    __CFGenericValidateType(key, CFStringGetTypeID());
771    // rule-based formatters don't do attributes and symbols, except for one
772    if (kCFNumberFormatterSpellOutStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return;
773    if (kCFNumberFormatterOrdinalStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return;
774    if (kCFNumberFormatterDurationStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return;
775    if (kCFNumberFormatterCurrencyCodeKey == key) {
776	__CFGenericValidateType(value, CFStringGetTypeID());
777	cnt = CFStringGetLength((CFStringRef)value);
778	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
779	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
780	__cficu_unum_setTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, cnt, &status);
781    } else if (kCFNumberFormatterDecimalSeparatorKey == key) {
782	__CFGenericValidateType(value, CFStringGetTypeID());
783	cnt = CFStringGetLength((CFStringRef)value);
784	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
785	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
786	__cficu_unum_setSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, cnt, &status);
787    } else if (kCFNumberFormatterCurrencyDecimalSeparatorKey == key) {
788	__CFGenericValidateType(value, CFStringGetTypeID());
789	cnt = CFStringGetLength((CFStringRef)value);
790	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
791	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
792	__cficu_unum_setSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, ubuffer, cnt, &status);
793    } else if (kCFNumberFormatterAlwaysShowDecimalSeparatorKey == key) {
794	__CFGenericValidateType(value, CFBooleanGetTypeID());
795	__cficu_unum_setAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN, (kCFBooleanTrue == value));
796    } else if (kCFNumberFormatterGroupingSeparatorKey == key) {
797	__CFGenericValidateType(value, CFStringGetTypeID());
798	cnt = CFStringGetLength((CFStringRef)value);
799	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
800	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
801	__cficu_unum_setSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, (const UChar *)ubuffer, cnt, &status);
802    } else if (kCFNumberFormatterUseGroupingSeparatorKey == key) {
803	__CFGenericValidateType(value, CFBooleanGetTypeID());
804	__cficu_unum_setAttribute(formatter->_nf, UNUM_GROUPING_USED, (kCFBooleanTrue == value));
805    } else if (kCFNumberFormatterPercentSymbolKey == key) {
806	__CFGenericValidateType(value, CFStringGetTypeID());
807	cnt = CFStringGetLength((CFStringRef)value);
808	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
809	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
810	__cficu_unum_setSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, cnt, &status);
811    } else if (kCFNumberFormatterZeroSymbolKey == key) {
812        __CFGenericValidateType(value, CFStringGetTypeID());
813        CFStringRef old = formatter->_zeroSym;
814        formatter->_zeroSym = value ? (CFStringRef)CFRetain(value) : NULL;
815        if (old) CFRelease(old);
816    } else if (kCFNumberFormatterNaNSymbolKey == key) {
817	__CFGenericValidateType(value, CFStringGetTypeID());
818	cnt = CFStringGetLength((CFStringRef)value);
819	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
820	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
821	__cficu_unum_setSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, cnt, &status);
822    } else if (kCFNumberFormatterInfinitySymbolKey == key) {
823	__CFGenericValidateType(value, CFStringGetTypeID());
824	cnt = CFStringGetLength((CFStringRef)value);
825	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
826	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
827	__cficu_unum_setSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, cnt, &status);
828    } else if (kCFNumberFormatterMinusSignKey == key) {
829	__CFGenericValidateType(value, CFStringGetTypeID());
830	cnt = CFStringGetLength((CFStringRef)value);
831	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
832	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
833	__cficu_unum_setSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, cnt, &status);
834    } else if (kCFNumberFormatterPlusSignKey == key) {
835	__CFGenericValidateType(value, CFStringGetTypeID());
836	cnt = CFStringGetLength((CFStringRef)value);
837	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
838	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
839	__cficu_unum_setSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, cnt, &status);
840    } else if (kCFNumberFormatterCurrencySymbolKey == key) {
841	__CFGenericValidateType(value, CFStringGetTypeID());
842	cnt = CFStringGetLength((CFStringRef)value);
843	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
844	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
845	__cficu_unum_setSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, (const UChar *)ubuffer, cnt, &status);
846    } else if (kCFNumberFormatterExponentSymbolKey == key) {
847	__CFGenericValidateType(value, CFStringGetTypeID());
848	cnt = CFStringGetLength((CFStringRef)value);
849	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
850	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
851	__cficu_unum_setSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, cnt, &status);
852    } else if (kCFNumberFormatterMinIntegerDigitsKey == key) {
853	__CFGenericValidateType(value, CFNumberGetTypeID());
854	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
855	__cficu_unum_setAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS, n);
856    } else if (kCFNumberFormatterMaxIntegerDigitsKey == key) {
857	__CFGenericValidateType(value, CFNumberGetTypeID());
858	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
859	__cficu_unum_setAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS, n);
860    } else if (kCFNumberFormatterMinFractionDigitsKey == key) {
861	__CFGenericValidateType(value, CFNumberGetTypeID());
862	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
863	__cficu_unum_setAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS, n);
864    } else if (kCFNumberFormatterMaxFractionDigitsKey == key) {
865	__CFGenericValidateType(value, CFNumberGetTypeID());
866	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
867	__cficu_unum_setAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS, n);
868    } else if (kCFNumberFormatterGroupingSizeKey == key) {
869	__CFGenericValidateType(value, CFNumberGetTypeID());
870	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
871	__cficu_unum_setAttribute(formatter->_nf, UNUM_GROUPING_SIZE, n);
872    } else if (kCFNumberFormatterSecondaryGroupingSizeKey == key) {
873	__CFGenericValidateType(value, CFNumberGetTypeID());
874	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
875	__cficu_unum_setAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE, n);
876    } else if (kCFNumberFormatterRoundingModeKey == key) {
877	__CFGenericValidateType(value, CFNumberGetTypeID());
878	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
879	__cficu_unum_setAttribute(formatter->_nf, UNUM_ROUNDING_MODE, n);
880    } else if (kCFNumberFormatterRoundingIncrementKey == key) {
881	__CFGenericValidateType(value, CFNumberGetTypeID());
882	CFNumberGetValue((CFNumberRef)value, kCFNumberDoubleType, &d);
883	__cficu_unum_setDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT, d);
884    } else if (kCFNumberFormatterFormatWidthKey == key) {
885	__CFGenericValidateType(value, CFNumberGetTypeID());
886	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
887	__cficu_unum_setAttribute(formatter->_nf, UNUM_FORMAT_WIDTH, n);
888    } else if (kCFNumberFormatterPaddingPositionKey == key) {
889	__CFGenericValidateType(value, CFNumberGetTypeID());
890	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
891	__cficu_unum_setAttribute(formatter->_nf, UNUM_PADDING_POSITION, n);
892    } else if (kCFNumberFormatterPaddingCharacterKey == key) {
893	__CFGenericValidateType(value, CFStringGetTypeID());
894	cnt = CFStringGetLength((CFStringRef)value);
895	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
896	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
897	__cficu_unum_setTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, cnt, &status);
898    } else if (kCFNumberFormatterDefaultFormatKey == key) {
899	// read-only attribute
900    } else if (kCFNumberFormatterMultiplierKey == key) {
901	__CFGenericValidateType(value, CFNumberGetTypeID());
902        CFNumberRef old = formatter->_multiplier;
903        formatter->_multiplier = value ? (CFNumberRef)CFRetain(value) : NULL;
904	formatter->_userSetMultiplier = value ? true : false;
905        if (old) CFRelease(old);
906    } else if (kCFNumberFormatterPositivePrefixKey == key) {
907	__CFGenericValidateType(value, CFStringGetTypeID());
908	cnt = CFStringGetLength((CFStringRef)value);
909	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
910	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
911	__cficu_unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, cnt, &status);
912    } else if (kCFNumberFormatterPositiveSuffixKey == key) {
913	__CFGenericValidateType(value, CFStringGetTypeID());
914	cnt = CFStringGetLength((CFStringRef)value);
915	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
916	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
917	__cficu_unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status);
918    } else if (kCFNumberFormatterNegativePrefixKey == key) {
919	__CFGenericValidateType(value, CFStringGetTypeID());
920	cnt = CFStringGetLength((CFStringRef)value);
921	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
922	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
923	__cficu_unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, cnt, &status);
924    } else if (kCFNumberFormatterNegativeSuffixKey == key) {
925	__CFGenericValidateType(value, CFStringGetTypeID());
926	cnt = CFStringGetLength((CFStringRef)value);
927	if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
928	CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
929	__cficu_unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status);
930    } else if (kCFNumberFormatterPerMillSymbolKey == key) {
931	__CFGenericValidateType(value, CFStringGetTypeID());
932        cnt = CFStringGetLength((CFStringRef)value);
933        if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
934        CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
935        __cficu_unum_setSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, cnt, &status);
936    } else if (kCFNumberFormatterInternationalCurrencySymbolKey == key) {
937	__CFGenericValidateType(value, CFStringGetTypeID());
938        cnt = CFStringGetLength((CFStringRef)value);
939        if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
940        CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
941        __cficu_unum_setSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, cnt, &status);
942    } else if (kCFNumberFormatterCurrencyGroupingSeparatorKey == key) {
943	__CFGenericValidateType(value, CFStringGetTypeID());
944        cnt = CFStringGetLength((CFStringRef)value);
945        if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
946        CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer);
947        __cficu_unum_setSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, cnt, &status);
948    } else if (kCFNumberFormatterIsLenientKey == key) {
949	__CFGenericValidateType(value, CFBooleanGetTypeID());
950	formatter->_isLenient = (kCFBooleanTrue == value);
951	__cficu_unum_setAttribute(formatter->_nf, UNUM_LENIENT_PARSE, (kCFBooleanTrue == value));
952    } else if (kCFNumberFormatterUseSignificantDigitsKey == key) {
953	__CFGenericValidateType(value, CFBooleanGetTypeID());
954	__cficu_unum_setAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED, (kCFBooleanTrue == value));
955    } else if (kCFNumberFormatterMinSignificantDigitsKey == key) {
956	__CFGenericValidateType(value, CFNumberGetTypeID());
957	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
958	__cficu_unum_setAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS, n);
959    } else if (kCFNumberFormatterMaxSignificantDigitsKey == key) {
960	__CFGenericValidateType(value, CFNumberGetTypeID());
961	CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n);
962	__cficu_unum_setAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS, n);
963    } else if (kCFNumberFormatterUsesCharacterDirectionKey == key) {
964        __CFGenericValidateType(value, CFBooleanGetTypeID());
965        formatter->_usesCharacterDirection = value == kCFBooleanTrue;
966    } else {
967	CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
968    }
969    if (_CFExecutableLinkedOnOrAfter(CFSystemVersionSnowLeopard)) {
970        // do a dummy call to CFNumberFormatterGetFormat() after changing an attribute because
971        // ICU sometimes changes the pattern due to a property change, and we need to poke
972        // __cficu_unum_toPattern() and also update our own variables
973        CFNumberFormatterGetFormat(formatter);
974    }
975}
976
977CFTypeRef CFNumberFormatterCopyProperty(CFNumberFormatterRef formatter, CFStringRef key) {
978    int32_t n;
979    double d;
980    UErrorCode status = U_ZERO_ERROR;
981    UChar ubuffer[BUFFER_SIZE];
982    CFIndex cnt;
983    __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID());
984    __CFGenericValidateType(key, CFStringGetTypeID());
985    // rule-based formatters don't do attributes and symbols, except for one
986    if (kCFNumberFormatterSpellOutStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL;
987    if (kCFNumberFormatterOrdinalStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL;
988    if (kCFNumberFormatterDurationStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL;
989    if (kCFNumberFormatterCurrencyCodeKey == key) {
990	cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status);
991	if (U_SUCCESS(status) && cnt == 0) {
992	    CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
993	    char buffer[BUFFER_SIZE];
994	    const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
995	    if (NULL == cstr) {
996		if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
997	    }
998	    if (NULL == cstr) {
999	        return NULL;
1000	    }
1001	    UErrorCode status = U_ZERO_ERROR;
1002	    UNumberFormat *nf = __cficu_unum_open(UNUM_CURRENCY, NULL, 0, cstr, NULL, &status);
1003	    if (NULL != nf) {
1004		cnt = __cficu_unum_getTextAttribute(nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status);
1005		__cficu_unum_close(nf);
1006	    }
1007	}
1008	if (U_SUCCESS(status) && 0 < cnt && cnt <= BUFFER_SIZE) {
1009	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1010	}
1011    } else if (kCFNumberFormatterDecimalSeparatorKey == key) {
1012	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1013	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1014	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1015	}
1016    } else if (kCFNumberFormatterCurrencyDecimalSeparatorKey == key) {
1017	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, (UChar *)ubuffer, BUFFER_SIZE, &status);
1018	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1019	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1020	}
1021    } else if (kCFNumberFormatterAlwaysShowDecimalSeparatorKey == key) {
1022	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN);
1023	if (1) {
1024	    return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse);
1025	}
1026    } else if (kCFNumberFormatterGroupingSeparatorKey == key) {
1027	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1028	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1029	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1030	}
1031    } else if (kCFNumberFormatterUseGroupingSeparatorKey == key) {
1032	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_GROUPING_USED);
1033	if (1) {
1034	    return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse);
1035	}
1036   } else if (kCFNumberFormatterPercentSymbolKey == key) {
1037	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1038	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1039	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1040	}
1041    } else if (kCFNumberFormatterZeroSymbolKey == key) {
1042        return formatter->_zeroSym ? CFRetain(formatter->_zeroSym) : NULL;
1043    } else if (kCFNumberFormatterNaNSymbolKey == key) {
1044	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1045	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1046	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1047	}
1048    } else if (kCFNumberFormatterInfinitySymbolKey == key) {
1049	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1050	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1051	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1052	}
1053    } else if (kCFNumberFormatterMinusSignKey == key) {
1054	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1055	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1056	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1057	}
1058    } else if (kCFNumberFormatterPlusSignKey == key) {
1059	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1060	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1061	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1062	}
1063    } else if (kCFNumberFormatterCurrencySymbolKey == key) {
1064	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1065	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1066	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1067	}
1068    } else if (kCFNumberFormatterExponentSymbolKey == key) {
1069	cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1070	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1071	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1072	}
1073    } else if (kCFNumberFormatterMinIntegerDigitsKey == key) {
1074	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS);
1075	if (1) {
1076	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1077	}
1078    } else if (kCFNumberFormatterMaxIntegerDigitsKey == key) {
1079	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS);
1080	if (1) {
1081	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1082	}
1083    } else if (kCFNumberFormatterMinFractionDigitsKey == key) {
1084	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS);
1085	if (1) {
1086	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1087	}
1088    } else if (kCFNumberFormatterMaxFractionDigitsKey == key) {
1089	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS);
1090	if (1) {
1091	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1092	}
1093    } else if (kCFNumberFormatterGroupingSizeKey == key) {
1094	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_GROUPING_SIZE);
1095	if (1) {
1096	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1097	}
1098    } else if (kCFNumberFormatterSecondaryGroupingSizeKey == key) {
1099	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE);
1100	if (1) {
1101	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1102	}
1103    } else if (kCFNumberFormatterRoundingModeKey == key) {
1104	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_ROUNDING_MODE);
1105	if (1) {
1106	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1107	}
1108    } else if (kCFNumberFormatterRoundingIncrementKey == key) {
1109	d = __cficu_unum_getDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT);
1110	if (1) {
1111	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberDoubleType, &d);
1112	}
1113    } else if (kCFNumberFormatterFormatWidthKey == key) {
1114	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_FORMAT_WIDTH);
1115	if (1) {
1116	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1117	}
1118    } else if (kCFNumberFormatterPaddingPositionKey == key) {
1119	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_PADDING_POSITION);
1120	if (1) {
1121	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1122	}
1123    } else if (kCFNumberFormatterPaddingCharacterKey == key) {
1124	cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, BUFFER_SIZE, &status);
1125	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1126	    return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1127	}
1128    } else if (kCFNumberFormatterDefaultFormatKey == key) {
1129	return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL;
1130    } else if (kCFNumberFormatterMultiplierKey == key) {
1131        return formatter->_multiplier ? CFRetain(formatter->_multiplier) : NULL;
1132    } else if (kCFNumberFormatterPositivePrefixKey == key) {
1133        cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, BUFFER_SIZE, &status);
1134        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1135            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1136        }
1137    } else if (kCFNumberFormatterPositiveSuffixKey == key) {
1138        cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status);
1139        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1140            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1141        }
1142    } else if (kCFNumberFormatterNegativePrefixKey == key) {
1143        cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, BUFFER_SIZE, &status);
1144        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1145            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1146        }
1147    } else if (kCFNumberFormatterNegativeSuffixKey == key) {
1148        cnt = __cficu_unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status);
1149        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1150            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1151        }
1152    } else if (kCFNumberFormatterPerMillSymbolKey == key) {
1153        cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1154        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1155            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1156        }
1157    } else if (kCFNumberFormatterInternationalCurrencySymbolKey == key) {
1158        cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1159        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1160            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1161        }
1162    } else if (kCFNumberFormatterCurrencyGroupingSeparatorKey == key) {
1163        cnt = __cficu_unum_getSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status);
1164        if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1165            return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt);
1166        }
1167    } else if (kCFNumberFormatterIsLenientKey == key) {
1168	// __cficu_unum_getAttribute(, UNUM_LENIENT_PARSE) is undefined.
1169	return CFRetain(formatter->_isLenient ? kCFBooleanTrue : kCFBooleanFalse);
1170    } else if (kCFNumberFormatterUseSignificantDigitsKey == key) {
1171	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED);
1172	if (1) {
1173	    return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse);
1174	}
1175    } else if (kCFNumberFormatterMinSignificantDigitsKey == key) {
1176	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS);
1177	if (1) {
1178	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1179	}
1180    } else if (kCFNumberFormatterMaxSignificantDigitsKey == key) {
1181	n = __cficu_unum_getAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS);
1182	if (1) {
1183	    return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n);
1184	}
1185    } else {
1186	CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
1187    }
1188    return NULL;
1189}
1190
1191
1192Boolean CFNumberFormatterGetDecimalInfoForCurrencyCode(CFStringRef currencyCode, int32_t *defaultFractionDigits, double *roundingIncrement) {
1193    UChar ubuffer[4];
1194    __CFGenericValidateType(currencyCode, CFStringGetTypeID());
1195    CFAssert1(3 == CFStringGetLength(currencyCode), __kCFLogAssertion, "%s(): currencyCode is not 3 characters", __PRETTY_FUNCTION__);
1196    CFStringGetCharacters(currencyCode, CFRangeMake(0, 3), (UniChar *)ubuffer);
1197    ubuffer[3] = 0;
1198    UErrorCode icuStatus = U_ZERO_ERROR;
1199    if (defaultFractionDigits) *defaultFractionDigits = __cficu_ucurr_getDefaultFractionDigits(ubuffer, &icuStatus);
1200    if (roundingIncrement) *roundingIncrement = __cficu_ucurr_getRoundingIncrement(ubuffer, &icuStatus);
1201    if (U_FAILURE(icuStatus))
1202        return false;
1203    return (!defaultFractionDigits || 0 <= *defaultFractionDigits) && (!roundingIncrement || 0.0 <= *roundingIncrement);
1204}
1205
1206#undef BUFFER_SIZE
1207
1208