]>
Commit | Line | Data |
---|---|---|
bd5b749c | 1 | /* |
8ca704e1 | 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. |
bd5b749c A |
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 | */ | |
f64f9b69 | 23 | |
bd5b749c | 24 | /* CFNumberFormatter.c |
8ca704e1 A |
25 | Copyright (c) 2002-2011, Apple Inc. All rights reserved. |
26 | Responsibility: David Smith | |
bd5b749c A |
27 | */ |
28 | ||
29 | #include <CoreFoundation/CFNumberFormatter.h> | |
8ca704e1 | 30 | #include <CoreFoundation/ForFoundationOnly.h> |
bd5b749c | 31 | #include "CFInternal.h" |
cf7d2af9 | 32 | #include "CFLocaleInternal.h" |
bd5b749c A |
33 | #include <unicode/unum.h> |
34 | #include <unicode/ucurr.h> | |
35 | #include <math.h> | |
36 | #include <float.h> | |
37 | ||
38 | static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter); | |
cf7d2af9 A |
39 | static CFStringRef __CFNumberFormatterCreateCompressedString(CFStringRef inString, Boolean isFormat, CFRange *rangep); |
40 | static UErrorCode __CFNumberFormatterApplyPattern(CFNumberFormatterRef formatter, CFStringRef pattern); | |
bd5b749c A |
41 | |
42 | #define BUFFER_SIZE 768 | |
43 | ||
44 | struct __CFNumberFormatter { | |
45 | CFRuntimeBase _base; | |
46 | UNumberFormat *_nf; | |
47 | CFLocaleRef _locale; | |
48 | CFNumberFormatterStyle _style; | |
49 | CFStringRef _format; // NULL for RBNFs | |
50 | CFStringRef _defformat; | |
cf7d2af9 | 51 | CFStringRef _compformat; |
bd5b749c A |
52 | CFNumberRef _multiplier; |
53 | CFStringRef _zeroSym; | |
cf7d2af9 A |
54 | Boolean _isLenient; |
55 | Boolean _userSetMultiplier; | |
bd5b749c A |
56 | }; |
57 | ||
58 | static CFStringRef __CFNumberFormatterCopyDescription(CFTypeRef cf) { | |
59 | CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf; | |
60 | return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFNumberFormatter %p [%p]>"), cf, CFGetAllocator(formatter)); | |
61 | } | |
62 | ||
63 | static void __CFNumberFormatterDeallocate(CFTypeRef cf) { | |
64 | CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf; | |
65 | if (formatter->_nf) unum_close(formatter->_nf); | |
66 | if (formatter->_locale) CFRelease(formatter->_locale); | |
67 | if (formatter->_format) CFRelease(formatter->_format); | |
68 | if (formatter->_defformat) CFRelease(formatter->_defformat); | |
cf7d2af9 | 69 | if (formatter->_compformat) CFRelease(formatter->_compformat); |
bd5b749c A |
70 | if (formatter->_multiplier) CFRelease(formatter->_multiplier); |
71 | if (formatter->_zeroSym) CFRelease(formatter->_zeroSym); | |
72 | } | |
73 | ||
74 | static CFTypeID __kCFNumberFormatterTypeID = _kCFRuntimeNotATypeID; | |
75 | ||
76 | static const CFRuntimeClass __CFNumberFormatterClass = { | |
77 | 0, | |
78 | "CFNumberFormatter", | |
79 | NULL, // init | |
80 | NULL, // copy | |
81 | __CFNumberFormatterDeallocate, | |
82 | NULL, | |
83 | NULL, | |
84 | NULL, // | |
85 | __CFNumberFormatterCopyDescription | |
86 | }; | |
87 | ||
88 | static void __CFNumberFormatterInitialize(void) { | |
89 | __kCFNumberFormatterTypeID = _CFRuntimeRegisterClass(&__CFNumberFormatterClass); | |
90 | } | |
91 | ||
92 | CFTypeID CFNumberFormatterGetTypeID(void) { | |
93 | if (_kCFRuntimeNotATypeID == __kCFNumberFormatterTypeID) __CFNumberFormatterInitialize(); | |
94 | return __kCFNumberFormatterTypeID; | |
95 | } | |
96 | ||
97 | CFNumberFormatterRef CFNumberFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFNumberFormatterStyle style) { | |
98 | struct __CFNumberFormatter *memory; | |
99 | uint32_t size = sizeof(struct __CFNumberFormatter) - sizeof(CFRuntimeBase); | |
100 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
101 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
102 | __CFGenericValidateType(locale, CFLocaleGetTypeID()); | |
103 | memory = (struct __CFNumberFormatter *)_CFRuntimeCreateInstance(allocator, CFNumberFormatterGetTypeID(), size, NULL); | |
104 | if (NULL == memory) { | |
105 | return NULL; | |
106 | } | |
107 | memory->_nf = NULL; | |
108 | memory->_locale = NULL; | |
109 | memory->_format = NULL; | |
110 | memory->_defformat = NULL; | |
cf7d2af9 | 111 | memory->_compformat = NULL; |
bd5b749c A |
112 | memory->_multiplier = NULL; |
113 | memory->_zeroSym = NULL; | |
cf7d2af9 A |
114 | memory->_isLenient = false; |
115 | memory->_userSetMultiplier = false; | |
bd5b749c A |
116 | if (NULL == locale) locale = CFLocaleGetSystem(); |
117 | memory->_style = style; | |
118 | uint32_t ustyle; | |
119 | switch (style) { | |
120 | case kCFNumberFormatterNoStyle: ustyle = UNUM_IGNORE; break; | |
121 | case kCFNumberFormatterDecimalStyle: ustyle = UNUM_DECIMAL; break; | |
122 | case kCFNumberFormatterCurrencyStyle: ustyle = UNUM_CURRENCY; break; | |
123 | case kCFNumberFormatterPercentStyle: ustyle = UNUM_PERCENT; break; | |
124 | case kCFNumberFormatterScientificStyle: ustyle = UNUM_SCIENTIFIC; break; | |
125 | case kCFNumberFormatterSpellOutStyle: ustyle = UNUM_SPELLOUT; break; | |
8ca704e1 A |
126 | case kCFNumberFormatterOrdinalStyle: ustyle = UNUM_ORDINAL; break; |
127 | case kCFNumberFormatterDurationStyle: ustyle = UNUM_DURATION; break; | |
bd5b749c A |
128 | default: |
129 | CFAssert2(0, __kCFLogAssertion, "%s(): unknown style %d", __PRETTY_FUNCTION__, style); | |
130 | ustyle = UNUM_DECIMAL; | |
131 | memory->_style = kCFNumberFormatterDecimalStyle; | |
132 | break; | |
133 | } | |
134 | CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR(""); | |
135 | char buffer[BUFFER_SIZE]; | |
136 | const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); | |
137 | if (NULL == cstr) { | |
138 | if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
139 | } | |
140 | if (NULL == cstr) { | |
141 | CFRelease(memory); | |
142 | return NULL; | |
143 | } | |
144 | UErrorCode status = U_ZERO_ERROR; | |
145 | memory->_nf = unum_open((UNumberFormatStyle)ustyle, NULL, 0, cstr, NULL, &status); | |
146 | CFAssert2(memory->_nf, __kCFLogAssertion, "%s(): error (%d) creating number formatter", __PRETTY_FUNCTION__, status); | |
147 | if (NULL == memory->_nf) { | |
148 | CFRelease(memory); | |
149 | return NULL; | |
150 | } | |
151 | UChar ubuff[4]; | |
152 | if (kCFNumberFormatterNoStyle == style) { | |
153 | status = U_ZERO_ERROR; | |
154 | ubuff[0] = '#'; ubuff[1] = ';'; ubuff[2] = '#'; | |
155 | unum_applyPattern(memory->_nf, false, ubuff, 3, NULL, &status); | |
156 | unum_setAttribute(memory->_nf, UNUM_MAX_INTEGER_DIGITS, 42); | |
157 | unum_setAttribute(memory->_nf, UNUM_MAX_FRACTION_DIGITS, 0); | |
158 | } | |
159 | memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : CFLocaleGetSystem(); | |
160 | __CFNumberFormatterCustomize(memory); | |
8ca704e1 | 161 | if (kCFNumberFormatterSpellOutStyle != memory->_style && kCFNumberFormatterOrdinalStyle != memory->_style && kCFNumberFormatterDurationStyle != memory->_style) { |
bd5b749c A |
162 | UChar ubuffer[BUFFER_SIZE]; |
163 | status = U_ZERO_ERROR; | |
164 | int32_t ret = unum_toPattern(memory->_nf, false, ubuffer, BUFFER_SIZE, &status); | |
165 | if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { | |
166 | memory->_format = CFStringCreateWithCharacters(allocator, (const UniChar *)ubuffer, ret); | |
167 | } | |
168 | } | |
169 | memory->_defformat = memory->_format ? (CFStringRef)CFRetain(memory->_format) : NULL; | |
cf7d2af9 | 170 | memory->_compformat = memory->_format ? __CFNumberFormatterCreateCompressedString(memory->_format, true, NULL) : NULL; |
8ca704e1 | 171 | if (kCFNumberFormatterSpellOutStyle != memory->_style && kCFNumberFormatterOrdinalStyle != memory->_style && kCFNumberFormatterDurationStyle != memory->_style) { |
bd5b749c A |
172 | int32_t n = unum_getAttribute(memory->_nf, UNUM_MULTIPLIER); |
173 | if (1 != n) { | |
174 | memory->_multiplier = CFNumberCreate(allocator, kCFNumberSInt32Type, &n); | |
175 | unum_setAttribute(memory->_nf, UNUM_MULTIPLIER, 1); | |
176 | } | |
177 | } | |
cf7d2af9 | 178 | unum_setAttribute(memory->_nf, UNUM_LENIENT_PARSE, 0); |
bd5b749c A |
179 | return (CFNumberFormatterRef)memory; |
180 | } | |
181 | ||
182 | extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); | |
183 | ||
184 | static void __substituteFormatStringFromPrefsNF(CFNumberFormatterRef formatter) { | |
185 | CFIndex formatStyle = formatter->_style; | |
186 | if (kCFNumberFormatterSpellOutStyle == formatStyle) return; | |
8ca704e1 A |
187 | if (kCFNumberFormatterOrdinalStyle == formatStyle) return; |
188 | if (kCFNumberFormatterDurationStyle == formatStyle) return; | |
bd5b749c A |
189 | CFStringRef prefName = CFSTR("AppleICUNumberFormatStrings"); |
190 | if (kCFNumberFormatterNoStyle != formatStyle) { | |
191 | CFStringRef pref = NULL; | |
192 | CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); | |
193 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, prefName) : NULL; | |
194 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
195 | CFStringRef key; | |
196 | switch (formatStyle) { | |
197 | case kCFNumberFormatterDecimalStyle: key = CFSTR("1"); break; | |
198 | case kCFNumberFormatterCurrencyStyle: key = CFSTR("2"); break; | |
199 | case kCFNumberFormatterPercentStyle: key = CFSTR("3"); break; | |
200 | case kCFNumberFormatterScientificStyle: key = CFSTR("4"); break; | |
201 | case kCFNumberFormatterSpellOutStyle: key = CFSTR("5"); break; | |
8ca704e1 A |
202 | case kCFNumberFormatterOrdinalStyle: key = CFSTR("6"); break; |
203 | case kCFNumberFormatterDurationStyle: key = CFSTR("7"); break; | |
bd5b749c A |
204 | default: key = CFSTR("0"); break; |
205 | } | |
206 | pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); | |
207 | } | |
208 | if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { | |
209 | int32_t icustyle = UNUM_IGNORE; | |
210 | switch (formatStyle) { | |
211 | case kCFNumberFormatterDecimalStyle: icustyle = UNUM_DECIMAL; break; | |
212 | case kCFNumberFormatterCurrencyStyle: icustyle = UNUM_CURRENCY; break; | |
213 | case kCFNumberFormatterPercentStyle: icustyle = UNUM_PERCENT; break; | |
214 | case kCFNumberFormatterScientificStyle: icustyle = UNUM_SCIENTIFIC; break; | |
215 | case kCFNumberFormatterSpellOutStyle: icustyle = UNUM_SPELLOUT; break; | |
8ca704e1 A |
216 | case kCFNumberFormatterOrdinalStyle: icustyle = UNUM_ORDINAL; break; |
217 | case kCFNumberFormatterDurationStyle: icustyle = UNUM_DURATION; break; | |
bd5b749c A |
218 | } |
219 | CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); | |
220 | char buffer[BUFFER_SIZE]; | |
221 | const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); | |
222 | if (NULL == cstr) { | |
223 | if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
224 | } | |
225 | UErrorCode status = U_ZERO_ERROR; | |
226 | UNumberFormat *nf = unum_open((UNumberFormatStyle)icustyle, NULL, 0, cstr, NULL, &status); | |
227 | if (NULL != nf) { | |
228 | UChar ubuffer[BUFFER_SIZE]; | |
229 | status = U_ZERO_ERROR; | |
230 | int32_t number_len = unum_toPattern(nf, false, ubuffer, BUFFER_SIZE, &status); | |
231 | if (U_SUCCESS(status) && number_len <= BUFFER_SIZE) { | |
232 | CFStringRef numberString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, number_len); | |
233 | status = U_ZERO_ERROR; | |
234 | int32_t formatter_len = unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status); | |
235 | if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) { | |
236 | CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); | |
237 | CFStringAppendCharacters(formatString, (const UniChar *)ubuffer, formatter_len); | |
238 | // find numberString inside formatString, substitute the pref in that range | |
239 | CFRange result; | |
240 | if (CFStringFindWithOptions(formatString, numberString, CFRangeMake(0, formatter_len), 0, &result)) { | |
241 | CFStringReplace(formatString, result, pref); | |
cf7d2af9 | 242 | __CFNumberFormatterApplyPattern(formatter, formatString); |
bd5b749c A |
243 | } |
244 | CFRelease(formatString); | |
245 | } | |
246 | CFRelease(numberString); | |
247 | } | |
248 | unum_close(nf); | |
249 | } | |
250 | } | |
251 | } | |
252 | } | |
253 | ||
cf7d2af9 A |
254 | static UniChar __CFNumberFormatterNormalizeCharacter(UniChar c) { |
255 | if (CFCharacterSetIsCharacterMember(CFCharacterSetGetPredefined(kCFCharacterSetWhitespace), c)) { | |
256 | return ' '; | |
257 | } else { | |
258 | return c; | |
259 | } | |
260 | } | |
261 | ||
262 | /* 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. */ | |
263 | static CFStringRef __CFNumberFormatterCreateCompressedString(CFStringRef inString, Boolean isFormat, CFRange *rangep) { | |
264 | if (!inString) return NULL; | |
265 | CFRange range = { 0, 0 }; | |
266 | if (rangep) { | |
267 | range = *rangep; | |
268 | } else { | |
269 | range.length = CFStringGetLength(inString); | |
270 | } | |
271 | CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); | |
272 | CFCharacterSetRef letters = CFCharacterSetGetPredefined(kCFCharacterSetLetter); | |
273 | CFCharacterSetRef numbers = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); | |
274 | UniChar prevCh = 0, nextCh = 0; | |
275 | Boolean inQuote = false; | |
276 | for (CFIndex in_idx = range.location; in_idx < range.location + range.length; in_idx++) { | |
277 | UniChar ch = __CFNumberFormatterNormalizeCharacter(CFStringGetCharacterAtIndex(inString, in_idx)); | |
278 | nextCh = (in_idx+1 < range.length) ? CFStringGetCharacterAtIndex(inString, in_idx+1) : 0; | |
279 | if (isFormat && ch == '\'') inQuote = !inQuote; | |
280 | if (inQuote || ch != ' ' || (CFCharacterSetIsCharacterMember(letters, prevCh) && CFCharacterSetIsCharacterMember(letters, nextCh)) || (CFCharacterSetIsCharacterMember(numbers, prevCh) && CFCharacterSetIsCharacterMember(numbers, nextCh))) { | |
281 | CFStringAppendCharacters(outString, &ch, 1); | |
282 | prevCh = ch; | |
283 | } | |
284 | } | |
285 | return outString; | |
286 | } | |
287 | ||
8ca704e1 | 288 | // Should not be called for rule-based ICU formatters; not supported |
bd5b749c A |
289 | static void __CFNumberFormatterApplySymbolPrefs(const void *key, const void *value, void *context) { |
290 | if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFStringGetTypeID()) { | |
291 | CFNumberFormatterRef formatter = (CFNumberFormatterRef)context; | |
292 | UNumberFormatSymbol sym = (UNumberFormatSymbol)CFStringGetIntValue((CFStringRef)key); | |
293 | CFStringRef item = (CFStringRef)value; | |
294 | CFIndex item_cnt = CFStringGetLength(item); | |
295 | STACK_BUFFER_DECL(UChar, item_buffer, item_cnt); | |
296 | UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); | |
297 | if (NULL == item_ustr) { | |
298 | CFStringGetCharacters(item, CFRangeMake(0, __CFMin(BUFFER_SIZE, item_cnt)), (UniChar *)item_buffer); | |
299 | item_ustr = item_buffer; | |
300 | } | |
301 | UErrorCode status = U_ZERO_ERROR; | |
302 | unum_setSymbol(formatter->_nf, sym, item_ustr, item_cnt, &status); | |
303 | } | |
304 | } | |
305 | ||
8ca704e1 | 306 | // Should not be called for rule-based ICU formatters |
cf7d2af9 | 307 | static UErrorCode __CFNumberFormatterApplyPattern(CFNumberFormatterRef formatter, CFStringRef pattern) { |
8ca704e1 A |
308 | if (kCFNumberFormatterSpellOutStyle == formatter->_style) return U_UNSUPPORTED_ERROR; |
309 | if (kCFNumberFormatterOrdinalStyle == formatter->_style) return U_UNSUPPORTED_ERROR; | |
310 | if (kCFNumberFormatterDurationStyle == formatter->_style) return U_UNSUPPORTED_ERROR; | |
cf7d2af9 A |
311 | CFIndex cnt = CFStringGetLength(pattern); |
312 | STACK_BUFFER_DECL(UChar, ubuffer, cnt); | |
313 | const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(pattern); | |
314 | if (NULL == ustr) { | |
315 | CFStringGetCharacters(pattern, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
316 | ustr = ubuffer; | |
317 | } | |
318 | UErrorCode status = U_ZERO_ERROR; | |
319 | unum_applyPattern(formatter->_nf, false, ustr, cnt, NULL, &status); | |
320 | ||
321 | // unum_applyPattern() may have magically changed other attributes based on | |
322 | // the contents of the format string; we simply expose that ICU behavior, except | |
323 | // for UNUM_MULTIPLIER, which we re-read and reset, like we did at initialization | |
324 | // time though any user-set multiplier state takes precedence. | |
325 | if (formatter->_userSetMultiplier) { | |
326 | unum_setAttribute(formatter->_nf, UNUM_MULTIPLIER, 1); | |
327 | } else { | |
328 | if (formatter->_multiplier) CFRelease(formatter->_multiplier); | |
329 | formatter->_multiplier = NULL; | |
330 | int32_t n = unum_getAttribute(formatter->_nf, UNUM_MULTIPLIER); | |
331 | if (1 != n) { | |
332 | formatter->_multiplier = CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
333 | unum_setAttribute(formatter->_nf, UNUM_MULTIPLIER, 1); | |
334 | } | |
335 | } | |
336 | return status; | |
337 | } | |
338 | ||
bd5b749c A |
339 | static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter) { |
340 | __substituteFormatStringFromPrefsNF(formatter); | |
341 | CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); | |
342 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUNumberSymbols")) : NULL; | |
343 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
344 | CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFNumberFormatterApplySymbolPrefs, formatter); | |
345 | } | |
346 | } | |
347 | ||
348 | CFLocaleRef CFNumberFormatterGetLocale(CFNumberFormatterRef formatter) { | |
349 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
350 | return formatter->_locale; | |
351 | } | |
352 | ||
353 | CFNumberFormatterStyle CFNumberFormatterGetStyle(CFNumberFormatterRef formatter) { | |
354 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
355 | return formatter->_style; | |
356 | } | |
357 | ||
358 | CFStringRef CFNumberFormatterGetFormat(CFNumberFormatterRef formatter) { | |
359 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
360 | if (kCFNumberFormatterSpellOutStyle == formatter->_style) return NULL; | |
8ca704e1 A |
361 | if (kCFNumberFormatterOrdinalStyle == formatter->_style) return NULL; |
362 | if (kCFNumberFormatterDurationStyle == formatter->_style) return NULL; | |
bd5b749c A |
363 | UChar ubuffer[BUFFER_SIZE]; |
364 | CFStringRef newString = NULL; | |
365 | UErrorCode status = U_ZERO_ERROR; | |
366 | int32_t ret = unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status); | |
367 | if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { | |
368 | newString = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ret); | |
369 | } | |
370 | if (newString && !formatter->_format) { | |
371 | formatter->_format = newString; | |
cf7d2af9 A |
372 | if (formatter->_compformat) CFRelease(formatter->_compformat); |
373 | formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL); | |
bd5b749c A |
374 | } else if (newString && !CFEqual(newString, formatter->_format)) { |
375 | CFRelease(formatter->_format); | |
376 | formatter->_format = newString; | |
cf7d2af9 A |
377 | if (formatter->_compformat) CFRelease(formatter->_compformat); |
378 | formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL); | |
bd5b749c A |
379 | } else if (newString) { |
380 | CFRelease(newString); | |
381 | } | |
382 | return formatter->_format; | |
383 | } | |
384 | ||
385 | void CFNumberFormatterSetFormat(CFNumberFormatterRef formatter, CFStringRef formatString) { | |
386 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
387 | __CFGenericValidateType(formatString, CFStringGetTypeID()); | |
388 | if (kCFNumberFormatterSpellOutStyle == formatter->_style) return; | |
8ca704e1 A |
389 | if (kCFNumberFormatterOrdinalStyle == formatter->_style) return; |
390 | if (kCFNumberFormatterDurationStyle == formatter->_style) return; | |
bd5b749c A |
391 | CFIndex cnt = CFStringGetLength(formatString); |
392 | CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); | |
393 | if ((!formatter->_format || !CFEqual(formatter->_format, formatString)) && cnt <= 1024) { | |
cf7d2af9 | 394 | UErrorCode status = __CFNumberFormatterApplyPattern(formatter, formatString); |
bd5b749c | 395 | if (U_SUCCESS(status)) { |
bd5b749c A |
396 | UChar ubuffer2[BUFFER_SIZE]; |
397 | status = U_ZERO_ERROR; | |
398 | int32_t ret = unum_toPattern(formatter->_nf, false, ubuffer2, BUFFER_SIZE, &status); | |
399 | if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { | |
cf7d2af9 | 400 | if (formatter->_format) CFRelease(formatter->_format); |
bd5b749c | 401 | formatter->_format = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer2, ret); |
cf7d2af9 A |
402 | if (formatter->_compformat) CFRelease(formatter->_compformat); |
403 | formatter->_compformat = __CFNumberFormatterCreateCompressedString(formatter->_format, true, NULL); | |
bd5b749c A |
404 | } |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | CFStringRef CFNumberFormatterCreateStringWithNumber(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberRef number) { | |
410 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
411 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
412 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
413 | __CFGenericValidateType(number, CFNumberGetTypeID()); | |
414 | CFNumberType type = CFNumberGetType(number); | |
415 | char buffer[64]; | |
416 | CFNumberGetValue(number, type, buffer); | |
417 | return CFNumberFormatterCreateStringWithValue(allocator, formatter, type, buffer); | |
418 | } | |
419 | ||
420 | #define FORMAT(T, FUNC) \ | |
421 | T value = *(T *)valuePtr; \ | |
422 | if (0 == value && formatter->_zeroSym) { return (CFStringRef)CFRetain(formatter->_zeroSym); } \ | |
cf7d2af9 A |
423 | if (1.0 != multiplier) { \ |
424 | double dummy; \ | |
425 | if (modf(multiplier, &dummy) < FLT_EPSILON) { /* float epsilon specifically chosen cuz it is a bit bigger */ \ | |
426 | value = value * (T)floor(multiplier); \ | |
427 | } else { \ | |
428 | value = (T)(value * multiplier); \ | |
429 | } \ | |
430 | } \ | |
bd5b749c A |
431 | status = U_ZERO_ERROR; \ |
432 | used = FUNC(formatter->_nf, value, ubuffer, cnt, NULL, &status); \ | |
433 | if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { \ | |
434 | cnt = used + 1; \ | |
435 | ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); \ | |
436 | status = U_ZERO_ERROR; \ | |
437 | used = FUNC(formatter->_nf, value, ustr, cnt, NULL, &status); \ | |
438 | } | |
439 | ||
440 | CFStringRef CFNumberFormatterCreateStringWithValue(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberType numberType, const void *valuePtr) { | |
441 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
442 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
443 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
444 | double multiplier = 1.0; | |
445 | if (formatter->_multiplier) { | |
446 | if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { | |
447 | multiplier = 1.0; | |
448 | } | |
449 | } | |
450 | UChar *ustr = NULL, ubuffer[BUFFER_SIZE]; | |
451 | UErrorCode status = U_ZERO_ERROR; | |
452 | CFIndex used, cnt = BUFFER_SIZE; | |
453 | if (numberType == kCFNumberFloat64Type || numberType == kCFNumberDoubleType) { | |
454 | FORMAT(double, unum_formatDouble) | |
455 | } else if (numberType == kCFNumberFloat32Type || numberType == kCFNumberFloatType) { | |
456 | FORMAT(float, unum_formatDouble) | |
457 | } else if (numberType == kCFNumberSInt64Type || numberType == kCFNumberLongLongType) { | |
458 | FORMAT(int64_t, unum_formatInt64) | |
459 | } else if (numberType == kCFNumberLongType || numberType == kCFNumberCFIndexType) { | |
460 | #if __LP64__ | |
461 | FORMAT(int64_t, unum_formatInt64) | |
462 | #else | |
463 | FORMAT(int32_t, unum_formatInt64) | |
464 | #endif | |
465 | } else if (numberType == kCFNumberSInt32Type || numberType == kCFNumberIntType) { | |
466 | FORMAT(int32_t, unum_formatInt64) | |
467 | } else if (numberType == kCFNumberSInt16Type || numberType == kCFNumberShortType) { | |
468 | FORMAT(int16_t, unum_formatInt64) | |
469 | } else if (numberType == kCFNumberSInt8Type || numberType == kCFNumberCharType) { | |
470 | FORMAT(int8_t, unum_formatInt64) | |
471 | } else { | |
472 | CFAssert2(0, __kCFLogAssertion, "%s(): unknown CFNumberType (%d)", __PRETTY_FUNCTION__, numberType); | |
473 | return NULL; | |
474 | } | |
475 | CFStringRef string = NULL; | |
476 | if (U_SUCCESS(status)) { | |
477 | string = CFStringCreateWithCharacters(allocator, ustr ? (const UniChar *)ustr : (const UniChar *)ubuffer, used); | |
478 | } | |
479 | if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); | |
480 | return string; | |
481 | } | |
482 | ||
483 | #undef FORMAT | |
484 | ||
485 | CFNumberRef CFNumberFormatterCreateNumberFromString(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFOptionFlags options) { | |
486 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
487 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
488 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
489 | __CFGenericValidateType(string, CFStringGetTypeID()); | |
490 | CFNumberType type = (options & kCFNumberFormatterParseIntegersOnly) ? kCFNumberSInt64Type : kCFNumberFloat64Type; | |
491 | char buffer[16]; | |
492 | if (CFNumberFormatterGetValueFromString(formatter, string, rangep, type, buffer)) { | |
493 | return CFNumberCreate(allocator, type, buffer); | |
494 | } | |
495 | return NULL; | |
496 | } | |
497 | ||
498 | Boolean CFNumberFormatterGetValueFromString(CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFNumberType numberType, void *valuePtr) { | |
499 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
500 | __CFGenericValidateType(string, CFStringGetTypeID()); | |
cf7d2af9 | 501 | CFStringRef stringToParse = formatter->_isLenient ? __CFNumberFormatterCreateCompressedString(string, false, rangep) : (CFStringRef)CFRetain(string); |
bd5b749c | 502 | CFRange range = {0, 0}; |
cf7d2af9 A |
503 | if(formatter->_isLenient) { |
504 | range.length = CFStringGetLength(stringToParse); | |
bd5b749c | 505 | } else { |
cf7d2af9 A |
506 | if (rangep) { |
507 | range = *rangep; | |
508 | } else { | |
509 | range.length = CFStringGetLength(stringToParse); | |
510 | } | |
511 | // unum_parse chokes on leading whitespace | |
512 | CFCharacterSetRef whitespace = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace); | |
513 | while(range.length > 0 && CFCharacterSetIsCharacterMember(whitespace, CFStringGetCharacterAtIndex(stringToParse, range.location))) { | |
514 | range.location++; | |
515 | range.length--; | |
516 | } | |
bd5b749c | 517 | } |
cf7d2af9 A |
518 | Boolean isZero = false; |
519 | if (formatter->_zeroSym) { | |
520 | CFStringRef zeroSym = formatter->_isLenient ? __CFNumberFormatterCreateCompressedString(formatter->_zeroSym, false, NULL) : (CFStringRef)CFRetain(formatter->_zeroSym); | |
521 | if (kCFCompareEqualTo == CFStringCompare(stringToParse, zeroSym, 0)) { | |
522 | isZero = true; | |
523 | } | |
524 | CFRelease(zeroSym); | |
bd5b749c A |
525 | } |
526 | if (1024 < range.length) range.length = 1024; | |
cf7d2af9 | 527 | const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(stringToParse); |
bd5b749c A |
528 | STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1); |
529 | if (NULL == ustr) { | |
cf7d2af9 | 530 | CFStringGetCharacters(stringToParse, range, (UniChar *)ubuffer); |
bd5b749c | 531 | ustr = ubuffer; |
cf7d2af9 A |
532 | } else if (!formatter->_isLenient) { |
533 | ustr += range.location; | |
bd5b749c | 534 | } |
cf7d2af9 | 535 | if (formatter->_isLenient) __CFNumberFormatterApplyPattern(formatter, formatter->_compformat); |
bd5b749c A |
536 | Boolean integerOnly = 1; |
537 | switch (numberType) { | |
538 | case kCFNumberSInt8Type: case kCFNumberCharType: | |
539 | case kCFNumberSInt16Type: case kCFNumberShortType: | |
540 | case kCFNumberSInt32Type: case kCFNumberIntType: | |
541 | case kCFNumberLongType: case kCFNumberCFIndexType: | |
542 | case kCFNumberSInt64Type: case kCFNumberLongLongType: | |
8ca704e1 | 543 | unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 1); // ignored by ICU for rule-based formatters |
bd5b749c A |
544 | break; |
545 | default: | |
8ca704e1 | 546 | unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 0); // ignored by ICU for rule-based formatters |
bd5b749c A |
547 | integerOnly = 0; |
548 | break; | |
549 | } | |
550 | int32_t dpos = 0; | |
551 | UErrorCode status = U_ZERO_ERROR; | |
552 | int64_t dreti = 0; | |
553 | double dretd = 0.0; | |
554 | if (isZero) { | |
555 | dpos = rangep ? rangep->length : 0; | |
556 | } else { | |
557 | if (integerOnly) { | |
558 | dreti = unum_parseInt64(formatter->_nf, ustr, range.length, &dpos, &status); | |
559 | } else { | |
560 | dretd = unum_parseDouble(formatter->_nf, ustr, range.length, &dpos, &status); | |
561 | } | |
562 | } | |
cf7d2af9 A |
563 | if (formatter->_isLenient) { |
564 | if (rangep) { | |
565 | CFIndex uncompEnd = rangep->location + rangep->length; | |
566 | CFIndex uncompIdx = rangep->location; | |
567 | for (CFIndex compIdx = 0; compIdx < dpos && uncompIdx < uncompEnd; compIdx++, uncompIdx++) { | |
568 | while (uncompIdx < uncompEnd && ustr[compIdx] != __CFNumberFormatterNormalizeCharacter(CFStringGetCharacterAtIndex(string, uncompIdx))) uncompIdx++; | |
569 | } | |
570 | rangep->length = uncompIdx - rangep->location; | |
571 | } | |
572 | __CFNumberFormatterApplyPattern(formatter, formatter->_format); | |
573 | } else if (rangep) { | |
574 | rangep->length = dpos + (range.location - rangep->location); | |
575 | } | |
576 | CFRelease(stringToParse); | |
bd5b749c A |
577 | if (U_FAILURE(status)) { |
578 | return false; | |
579 | } | |
580 | if (formatter->_multiplier) { | |
581 | double multiplier = 1.0; | |
582 | if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { | |
583 | multiplier = 1.0; | |
584 | } | |
585 | dreti = (int64_t)((double)dreti / multiplier); // integer truncation | |
586 | dretd = dretd / multiplier; | |
587 | } | |
588 | switch (numberType) { | |
589 | case kCFNumberSInt8Type: case kCFNumberCharType: | |
590 | if (INT8_MIN <= dreti && dreti <= INT8_MAX) { | |
591 | *(int8_t *)valuePtr = (int8_t)dreti; | |
592 | return true; | |
593 | } | |
594 | break; | |
595 | case kCFNumberSInt16Type: case kCFNumberShortType: | |
596 | if (INT16_MIN <= dreti && dreti <= INT16_MAX) { | |
597 | *(int16_t *)valuePtr = (int16_t)dreti; | |
598 | return true; | |
599 | } | |
600 | break; | |
601 | case kCFNumberSInt32Type: case kCFNumberIntType: | |
602 | #if !__LP64__ | |
603 | case kCFNumberLongType: case kCFNumberCFIndexType: | |
604 | #endif | |
605 | if (INT32_MIN <= dreti && dreti <= INT32_MAX) { | |
606 | *(int32_t *)valuePtr = (int32_t)dreti; | |
607 | return true; | |
608 | } | |
609 | break; | |
610 | case kCFNumberSInt64Type: case kCFNumberLongLongType: | |
611 | #if __LP64__ | |
612 | case kCFNumberLongType: case kCFNumberCFIndexType: | |
613 | #endif | |
614 | if (INT64_MIN <= dreti && dreti <= INT64_MAX) { | |
615 | *(int64_t *)valuePtr = (int64_t)dreti; | |
616 | return true; | |
617 | } | |
618 | break; | |
619 | case kCFNumberFloat32Type: case kCFNumberFloatType: | |
620 | if (-FLT_MAX <= dretd && dretd <= FLT_MAX) { | |
621 | *(float *)valuePtr = (float)dretd; | |
622 | return true; | |
623 | } | |
624 | break; | |
625 | case kCFNumberFloat64Type: case kCFNumberDoubleType: | |
626 | if (-DBL_MAX <= dretd && dretd <= DBL_MAX) { | |
627 | *(double *)valuePtr = (double)dretd; | |
628 | return true; | |
629 | } | |
630 | break; | |
631 | } | |
632 | return false; | |
633 | } | |
634 | ||
635 | void CFNumberFormatterSetProperty(CFNumberFormatterRef formatter, CFStringRef key, CFTypeRef value) { | |
636 | int32_t n; | |
637 | double d; | |
638 | UErrorCode status = U_ZERO_ERROR; | |
639 | UChar ubuffer[BUFFER_SIZE]; | |
640 | CFIndex cnt; | |
641 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
642 | __CFGenericValidateType(key, CFStringGetTypeID()); | |
8ca704e1 A |
643 | // rule-based formatters don't do attributes and symbols, except for one |
644 | if (kCFNumberFormatterSpellOutStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; | |
645 | if (kCFNumberFormatterOrdinalStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; | |
646 | if (kCFNumberFormatterDurationStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return; | |
cf7d2af9 | 647 | if (kCFNumberFormatterCurrencyCodeKey == key) { |
bd5b749c A |
648 | __CFGenericValidateType(value, CFStringGetTypeID()); |
649 | cnt = CFStringGetLength((CFStringRef)value); | |
650 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
651 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
652 | unum_setTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, cnt, &status); | |
cf7d2af9 | 653 | } else if (kCFNumberFormatterDecimalSeparatorKey == key) { |
bd5b749c A |
654 | __CFGenericValidateType(value, CFStringGetTypeID()); |
655 | cnt = CFStringGetLength((CFStringRef)value); | |
656 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
657 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
658 | unum_setSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 659 | } else if (kCFNumberFormatterCurrencyDecimalSeparatorKey == key) { |
bd5b749c A |
660 | __CFGenericValidateType(value, CFStringGetTypeID()); |
661 | cnt = CFStringGetLength((CFStringRef)value); | |
662 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
663 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
664 | unum_setSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 665 | } else if (kCFNumberFormatterAlwaysShowDecimalSeparatorKey == key) { |
bd5b749c A |
666 | __CFGenericValidateType(value, CFBooleanGetTypeID()); |
667 | unum_setAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN, (kCFBooleanTrue == value)); | |
cf7d2af9 | 668 | } else if (kCFNumberFormatterGroupingSeparatorKey == key) { |
bd5b749c A |
669 | __CFGenericValidateType(value, CFStringGetTypeID()); |
670 | cnt = CFStringGetLength((CFStringRef)value); | |
671 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
672 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
673 | unum_setSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, (const UChar *)ubuffer, cnt, &status); | |
cf7d2af9 | 674 | } else if (kCFNumberFormatterUseGroupingSeparatorKey == key) { |
bd5b749c A |
675 | __CFGenericValidateType(value, CFBooleanGetTypeID()); |
676 | unum_setAttribute(formatter->_nf, UNUM_GROUPING_USED, (kCFBooleanTrue == value)); | |
cf7d2af9 | 677 | } else if (kCFNumberFormatterPercentSymbolKey == key) { |
bd5b749c A |
678 | __CFGenericValidateType(value, CFStringGetTypeID()); |
679 | cnt = CFStringGetLength((CFStringRef)value); | |
680 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
681 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
682 | unum_setSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 683 | } else if (kCFNumberFormatterZeroSymbolKey == key) { |
bd5b749c A |
684 | __CFGenericValidateType(value, CFStringGetTypeID()); |
685 | CFStringRef old = formatter->_zeroSym; | |
686 | formatter->_zeroSym = value ? (CFStringRef)CFRetain(value) : NULL; | |
687 | if (old) CFRelease(old); | |
cf7d2af9 | 688 | } else if (kCFNumberFormatterNaNSymbolKey == key) { |
bd5b749c A |
689 | __CFGenericValidateType(value, CFStringGetTypeID()); |
690 | cnt = CFStringGetLength((CFStringRef)value); | |
691 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
692 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
693 | unum_setSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 694 | } else if (kCFNumberFormatterInfinitySymbolKey == key) { |
bd5b749c A |
695 | __CFGenericValidateType(value, CFStringGetTypeID()); |
696 | cnt = CFStringGetLength((CFStringRef)value); | |
697 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
698 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
699 | unum_setSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 700 | } else if (kCFNumberFormatterMinusSignKey == key) { |
bd5b749c A |
701 | __CFGenericValidateType(value, CFStringGetTypeID()); |
702 | cnt = CFStringGetLength((CFStringRef)value); | |
703 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
704 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
705 | unum_setSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 706 | } else if (kCFNumberFormatterPlusSignKey == key) { |
bd5b749c A |
707 | __CFGenericValidateType(value, CFStringGetTypeID()); |
708 | cnt = CFStringGetLength((CFStringRef)value); | |
709 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
710 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
711 | unum_setSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 712 | } else if (kCFNumberFormatterCurrencySymbolKey == key) { |
bd5b749c A |
713 | __CFGenericValidateType(value, CFStringGetTypeID()); |
714 | cnt = CFStringGetLength((CFStringRef)value); | |
715 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
716 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
717 | unum_setSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, (const UChar *)ubuffer, cnt, &status); | |
cf7d2af9 | 718 | } else if (kCFNumberFormatterExponentSymbolKey == key) { |
bd5b749c A |
719 | __CFGenericValidateType(value, CFStringGetTypeID()); |
720 | cnt = CFStringGetLength((CFStringRef)value); | |
721 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
722 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
723 | unum_setSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 724 | } else if (kCFNumberFormatterMinIntegerDigitsKey == key) { |
bd5b749c A |
725 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
726 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
727 | unum_setAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS, n); | |
cf7d2af9 | 728 | } else if (kCFNumberFormatterMaxIntegerDigitsKey == key) { |
bd5b749c A |
729 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
730 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
731 | unum_setAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS, n); | |
cf7d2af9 | 732 | } else if (kCFNumberFormatterMinFractionDigitsKey == key) { |
bd5b749c A |
733 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
734 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
735 | unum_setAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS, n); | |
cf7d2af9 | 736 | } else if (kCFNumberFormatterMaxFractionDigitsKey == key) { |
bd5b749c A |
737 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
738 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
739 | unum_setAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS, n); | |
cf7d2af9 | 740 | } else if (kCFNumberFormatterGroupingSizeKey == key) { |
bd5b749c A |
741 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
742 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
743 | unum_setAttribute(formatter->_nf, UNUM_GROUPING_SIZE, n); | |
cf7d2af9 | 744 | } else if (kCFNumberFormatterSecondaryGroupingSizeKey == key) { |
bd5b749c A |
745 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
746 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
747 | unum_setAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE, n); | |
cf7d2af9 | 748 | } else if (kCFNumberFormatterRoundingModeKey == key) { |
bd5b749c A |
749 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
750 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
751 | unum_setAttribute(formatter->_nf, UNUM_ROUNDING_MODE, n); | |
cf7d2af9 | 752 | } else if (kCFNumberFormatterRoundingIncrementKey == key) { |
bd5b749c A |
753 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
754 | CFNumberGetValue((CFNumberRef)value, kCFNumberDoubleType, &d); | |
755 | unum_setDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT, d); | |
cf7d2af9 | 756 | } else if (kCFNumberFormatterFormatWidthKey == key) { |
bd5b749c A |
757 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
758 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
759 | unum_setAttribute(formatter->_nf, UNUM_FORMAT_WIDTH, n); | |
cf7d2af9 | 760 | } else if (kCFNumberFormatterPaddingPositionKey == key) { |
bd5b749c A |
761 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
762 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
763 | unum_setAttribute(formatter->_nf, UNUM_PADDING_POSITION, n); | |
cf7d2af9 | 764 | } else if (kCFNumberFormatterPaddingCharacterKey == key) { |
bd5b749c A |
765 | __CFGenericValidateType(value, CFStringGetTypeID()); |
766 | cnt = CFStringGetLength((CFStringRef)value); | |
767 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
768 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
769 | unum_setTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, cnt, &status); | |
cf7d2af9 | 770 | } else if (kCFNumberFormatterDefaultFormatKey == key) { |
bd5b749c | 771 | // read-only attribute |
cf7d2af9 | 772 | } else if (kCFNumberFormatterMultiplierKey == key) { |
bd5b749c A |
773 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
774 | CFNumberRef old = formatter->_multiplier; | |
775 | formatter->_multiplier = value ? (CFNumberRef)CFRetain(value) : NULL; | |
cf7d2af9 | 776 | formatter->_userSetMultiplier = value ? true : false; |
bd5b749c | 777 | if (old) CFRelease(old); |
cf7d2af9 | 778 | } else if (kCFNumberFormatterPositivePrefixKey == key) { |
bd5b749c A |
779 | __CFGenericValidateType(value, CFStringGetTypeID()); |
780 | cnt = CFStringGetLength((CFStringRef)value); | |
781 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
782 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
783 | unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, cnt, &status); | |
cf7d2af9 | 784 | } else if (kCFNumberFormatterPositiveSuffixKey == key) { |
bd5b749c A |
785 | __CFGenericValidateType(value, CFStringGetTypeID()); |
786 | cnt = CFStringGetLength((CFStringRef)value); | |
787 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
788 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
789 | unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status); | |
cf7d2af9 | 790 | } else if (kCFNumberFormatterNegativePrefixKey == key) { |
bd5b749c A |
791 | __CFGenericValidateType(value, CFStringGetTypeID()); |
792 | cnt = CFStringGetLength((CFStringRef)value); | |
793 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
794 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
795 | unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, cnt, &status); | |
cf7d2af9 | 796 | } else if (kCFNumberFormatterNegativeSuffixKey == key) { |
bd5b749c A |
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 | unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status); | |
cf7d2af9 | 802 | } else if (kCFNumberFormatterPerMillSymbolKey == key) { |
bd5b749c A |
803 | __CFGenericValidateType(value, CFStringGetTypeID()); |
804 | cnt = CFStringGetLength((CFStringRef)value); | |
805 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
806 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
807 | unum_setSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 808 | } else if (kCFNumberFormatterInternationalCurrencySymbolKey == key) { |
bd5b749c A |
809 | __CFGenericValidateType(value, CFStringGetTypeID()); |
810 | cnt = CFStringGetLength((CFStringRef)value); | |
811 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
812 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
813 | unum_setSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 814 | } else if (kCFNumberFormatterCurrencyGroupingSeparatorKey == key) { |
bd5b749c A |
815 | __CFGenericValidateType(value, CFStringGetTypeID()); |
816 | cnt = CFStringGetLength((CFStringRef)value); | |
817 | if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; | |
818 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
819 | unum_setSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, cnt, &status); | |
cf7d2af9 | 820 | } else if (kCFNumberFormatterIsLenientKey == key) { |
bd5b749c | 821 | __CFGenericValidateType(value, CFBooleanGetTypeID()); |
cf7d2af9 | 822 | formatter->_isLenient = (kCFBooleanTrue == value); |
bd5b749c | 823 | unum_setAttribute(formatter->_nf, UNUM_LENIENT_PARSE, (kCFBooleanTrue == value)); |
cf7d2af9 | 824 | } else if (kCFNumberFormatterUseSignificantDigitsKey == key) { |
bd5b749c A |
825 | __CFGenericValidateType(value, CFBooleanGetTypeID()); |
826 | unum_setAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED, (kCFBooleanTrue == value)); | |
cf7d2af9 | 827 | } else if (kCFNumberFormatterMinSignificantDigitsKey == key) { |
bd5b749c A |
828 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
829 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
830 | unum_setAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS, n); | |
cf7d2af9 | 831 | } else if (kCFNumberFormatterMaxSignificantDigitsKey == key) { |
bd5b749c A |
832 | __CFGenericValidateType(value, CFNumberGetTypeID()); |
833 | CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); | |
834 | unum_setAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS, n); | |
835 | } else { | |
836 | CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); | |
837 | } | |
cf7d2af9 A |
838 | if (_CFExecutableLinkedOnOrAfter(CFSystemVersionSnowLeopard)) { |
839 | // do a dummy call to CFNumberFormatterGetFormat() after changing an attribute because | |
840 | // ICU sometimes changes the pattern due to a property change, and we need to poke | |
841 | // unum_toPattern() and also update our own variables | |
842 | CFNumberFormatterGetFormat(formatter); | |
843 | } | |
bd5b749c A |
844 | } |
845 | ||
846 | CFTypeRef CFNumberFormatterCopyProperty(CFNumberFormatterRef formatter, CFStringRef key) { | |
847 | int32_t n; | |
848 | double d; | |
849 | UErrorCode status = U_ZERO_ERROR; | |
850 | UChar ubuffer[BUFFER_SIZE]; | |
851 | CFIndex cnt; | |
852 | __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); | |
853 | __CFGenericValidateType(key, CFStringGetTypeID()); | |
8ca704e1 A |
854 | // rule-based formatters don't do attributes and symbols, except for one |
855 | if (kCFNumberFormatterSpellOutStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; | |
856 | if (kCFNumberFormatterOrdinalStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; | |
857 | if (kCFNumberFormatterDurationStyle == formatter->_style && kCFNumberFormatterIsLenientKey != key) return NULL; | |
cf7d2af9 | 858 | if (kCFNumberFormatterCurrencyCodeKey == key) { |
bd5b749c A |
859 | cnt = unum_getTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status); |
860 | if (U_SUCCESS(status) && cnt == 0) { | |
861 | CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); | |
862 | char buffer[BUFFER_SIZE]; | |
863 | const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); | |
864 | if (NULL == cstr) { | |
865 | if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
866 | } | |
867 | if (NULL == cstr) { | |
868 | return NULL; | |
869 | } | |
870 | UErrorCode status = U_ZERO_ERROR; | |
871 | UNumberFormat *nf = unum_open(UNUM_CURRENCY, NULL, 0, cstr, NULL, &status); | |
872 | if (NULL != nf) { | |
873 | cnt = unum_getTextAttribute(nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status); | |
874 | unum_close(nf); | |
875 | } | |
876 | } | |
877 | if (U_SUCCESS(status) && 0 < cnt && cnt <= BUFFER_SIZE) { | |
878 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
879 | } | |
cf7d2af9 | 880 | } else if (kCFNumberFormatterDecimalSeparatorKey == key) { |
bd5b749c A |
881 | cnt = unum_getSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
882 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
883 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
884 | } | |
cf7d2af9 | 885 | } else if (kCFNumberFormatterCurrencyDecimalSeparatorKey == key) { |
bd5b749c A |
886 | cnt = unum_getSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, (UChar *)ubuffer, BUFFER_SIZE, &status); |
887 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
888 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
889 | } | |
cf7d2af9 | 890 | } else if (kCFNumberFormatterAlwaysShowDecimalSeparatorKey == key) { |
bd5b749c A |
891 | n = unum_getAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN); |
892 | if (1) { | |
893 | return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); | |
894 | } | |
cf7d2af9 | 895 | } else if (kCFNumberFormatterGroupingSeparatorKey == key) { |
bd5b749c A |
896 | cnt = unum_getSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
897 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
898 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
899 | } | |
cf7d2af9 | 900 | } else if (kCFNumberFormatterUseGroupingSeparatorKey == key) { |
bd5b749c A |
901 | n = unum_getAttribute(formatter->_nf, UNUM_GROUPING_USED); |
902 | if (1) { | |
903 | return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); | |
904 | } | |
cf7d2af9 | 905 | } else if (kCFNumberFormatterPercentSymbolKey == key) { |
bd5b749c A |
906 | cnt = unum_getSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
907 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
908 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
909 | } | |
cf7d2af9 | 910 | } else if (kCFNumberFormatterZeroSymbolKey == key) { |
bd5b749c | 911 | return formatter->_zeroSym ? CFRetain(formatter->_zeroSym) : NULL; |
cf7d2af9 | 912 | } else if (kCFNumberFormatterNaNSymbolKey == key) { |
bd5b749c A |
913 | cnt = unum_getSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
914 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
915 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
916 | } | |
cf7d2af9 | 917 | } else if (kCFNumberFormatterInfinitySymbolKey == key) { |
bd5b749c A |
918 | cnt = unum_getSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
919 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
920 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
921 | } | |
cf7d2af9 | 922 | } else if (kCFNumberFormatterMinusSignKey == key) { |
bd5b749c A |
923 | cnt = unum_getSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
924 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
925 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
926 | } | |
cf7d2af9 | 927 | } else if (kCFNumberFormatterPlusSignKey == key) { |
bd5b749c A |
928 | cnt = unum_getSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
929 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
930 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
931 | } | |
cf7d2af9 | 932 | } else if (kCFNumberFormatterCurrencySymbolKey == key) { |
bd5b749c A |
933 | cnt = unum_getSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
934 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
935 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
936 | } | |
cf7d2af9 | 937 | } else if (kCFNumberFormatterExponentSymbolKey == key) { |
bd5b749c A |
938 | cnt = unum_getSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
939 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
940 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
941 | } | |
cf7d2af9 | 942 | } else if (kCFNumberFormatterMinIntegerDigitsKey == key) { |
bd5b749c A |
943 | n = unum_getAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS); |
944 | if (1) { | |
945 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
946 | } | |
cf7d2af9 | 947 | } else if (kCFNumberFormatterMaxIntegerDigitsKey == key) { |
bd5b749c A |
948 | n = unum_getAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS); |
949 | if (1) { | |
950 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
951 | } | |
cf7d2af9 | 952 | } else if (kCFNumberFormatterMinFractionDigitsKey == key) { |
bd5b749c A |
953 | n = unum_getAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS); |
954 | if (1) { | |
955 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
956 | } | |
cf7d2af9 | 957 | } else if (kCFNumberFormatterMaxFractionDigitsKey == key) { |
bd5b749c A |
958 | n = unum_getAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS); |
959 | if (1) { | |
960 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
961 | } | |
cf7d2af9 | 962 | } else if (kCFNumberFormatterGroupingSizeKey == key) { |
bd5b749c A |
963 | n = unum_getAttribute(formatter->_nf, UNUM_GROUPING_SIZE); |
964 | if (1) { | |
965 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
966 | } | |
cf7d2af9 | 967 | } else if (kCFNumberFormatterSecondaryGroupingSizeKey == key) { |
bd5b749c A |
968 | n = unum_getAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE); |
969 | if (1) { | |
970 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
971 | } | |
cf7d2af9 | 972 | } else if (kCFNumberFormatterRoundingModeKey == key) { |
bd5b749c A |
973 | n = unum_getAttribute(formatter->_nf, UNUM_ROUNDING_MODE); |
974 | if (1) { | |
975 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
976 | } | |
cf7d2af9 | 977 | } else if (kCFNumberFormatterRoundingIncrementKey == key) { |
bd5b749c A |
978 | d = unum_getDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT); |
979 | if (1) { | |
980 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberDoubleType, &d); | |
981 | } | |
cf7d2af9 | 982 | } else if (kCFNumberFormatterFormatWidthKey == key) { |
bd5b749c A |
983 | n = unum_getAttribute(formatter->_nf, UNUM_FORMAT_WIDTH); |
984 | if (1) { | |
985 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
986 | } | |
cf7d2af9 | 987 | } else if (kCFNumberFormatterPaddingPositionKey == key) { |
bd5b749c A |
988 | n = unum_getAttribute(formatter->_nf, UNUM_PADDING_POSITION); |
989 | if (1) { | |
990 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
991 | } | |
cf7d2af9 | 992 | } else if (kCFNumberFormatterPaddingCharacterKey == key) { |
bd5b749c A |
993 | cnt = unum_getTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, BUFFER_SIZE, &status); |
994 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
995 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
996 | } | |
cf7d2af9 | 997 | } else if (kCFNumberFormatterDefaultFormatKey == key) { |
bd5b749c | 998 | return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL; |
cf7d2af9 | 999 | } else if (kCFNumberFormatterMultiplierKey == key) { |
bd5b749c | 1000 | return formatter->_multiplier ? CFRetain(formatter->_multiplier) : NULL; |
cf7d2af9 | 1001 | } else if (kCFNumberFormatterPositivePrefixKey == key) { |
bd5b749c A |
1002 | cnt = unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, BUFFER_SIZE, &status); |
1003 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1004 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1005 | } | |
cf7d2af9 | 1006 | } else if (kCFNumberFormatterPositiveSuffixKey == key) { |
bd5b749c A |
1007 | cnt = unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status); |
1008 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1009 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1010 | } | |
cf7d2af9 | 1011 | } else if (kCFNumberFormatterNegativePrefixKey == key) { |
bd5b749c A |
1012 | cnt = unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, BUFFER_SIZE, &status); |
1013 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1014 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1015 | } | |
cf7d2af9 | 1016 | } else if (kCFNumberFormatterNegativeSuffixKey == key) { |
bd5b749c A |
1017 | cnt = unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status); |
1018 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1019 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1020 | } | |
cf7d2af9 | 1021 | } else if (kCFNumberFormatterPerMillSymbolKey == key) { |
bd5b749c A |
1022 | cnt = unum_getSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
1023 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1024 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1025 | } | |
cf7d2af9 | 1026 | } else if (kCFNumberFormatterInternationalCurrencySymbolKey == key) { |
bd5b749c A |
1027 | cnt = unum_getSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
1028 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1029 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1030 | } | |
cf7d2af9 | 1031 | } else if (kCFNumberFormatterCurrencyGroupingSeparatorKey == key) { |
bd5b749c A |
1032 | cnt = unum_getSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); |
1033 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1034 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); | |
1035 | } | |
cf7d2af9 A |
1036 | } else if (kCFNumberFormatterIsLenientKey == key) { |
1037 | // unum_getAttribute(, UNUM_LENIENT_PARSE) is undefined. | |
1038 | return CFRetain(formatter->_isLenient ? kCFBooleanTrue : kCFBooleanFalse); | |
1039 | } else if (kCFNumberFormatterUseSignificantDigitsKey == key) { | |
bd5b749c A |
1040 | n = unum_getAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED); |
1041 | if (1) { | |
1042 | return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); | |
1043 | } | |
cf7d2af9 | 1044 | } else if (kCFNumberFormatterMinSignificantDigitsKey == key) { |
bd5b749c A |
1045 | n = unum_getAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS); |
1046 | if (1) { | |
1047 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
1048 | } | |
cf7d2af9 | 1049 | } else if (kCFNumberFormatterMaxSignificantDigitsKey == key) { |
bd5b749c A |
1050 | n = unum_getAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS); |
1051 | if (1) { | |
1052 | return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); | |
1053 | } | |
1054 | } else { | |
1055 | CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); | |
1056 | } | |
1057 | return NULL; | |
1058 | } | |
1059 | ||
bd5b749c A |
1060 | |
1061 | Boolean CFNumberFormatterGetDecimalInfoForCurrencyCode(CFStringRef currencyCode, int32_t *defaultFractionDigits, double *roundingIncrement) { | |
1062 | UChar ubuffer[4]; | |
1063 | __CFGenericValidateType(currencyCode, CFStringGetTypeID()); | |
1064 | CFAssert1(3 == CFStringGetLength(currencyCode), __kCFLogAssertion, "%s(): currencyCode is not 3 characters", __PRETTY_FUNCTION__); | |
1065 | CFStringGetCharacters(currencyCode, CFRangeMake(0, 3), (UniChar *)ubuffer); | |
1066 | ubuffer[3] = 0; | |
1067 | UErrorCode icuStatus = U_ZERO_ERROR; | |
1068 | if (defaultFractionDigits) *defaultFractionDigits = ucurr_getDefaultFractionDigits(ubuffer, &icuStatus); | |
1069 | if (roundingIncrement) *roundingIncrement = ucurr_getRoundingIncrement(ubuffer, &icuStatus); | |
1070 | if (U_FAILURE(icuStatus)) | |
1071 | return false; | |
1072 | return (!defaultFractionDigits || 0 <= *defaultFractionDigits) && (!roundingIncrement || 0.0 <= *roundingIncrement); | |
1073 | } | |
1074 | ||
1075 | #undef BUFFER_SIZE | |
1076 |