Change behavior of -numberFromString: and -stringFromNumber: to match Apple documentation. These are 10.4 and above methods and should not work with 10.0 ones.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@31949 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
stefanbidi 2011-01-25 02:25:32 +00:00
parent 4b40bd2e8c
commit c03e3a89fe
2 changed files with 374 additions and 345 deletions

View file

@ -1,3 +1,7 @@
2011-01-24 Stefan Bidigaray <stefanbidi@gmail.com>
* Source/NSNumberFormatter.m: Fixes for formatter behavior.
2011-01-24 Stefan Bidigaray <stefanbidi@gmail.com>
* Source/NSDateFormatter.m: Check ICU presence in private function.

View file

@ -199,8 +199,7 @@ _ICUToNSRoundingMode (UNumberFormatRoundingMode mode)
#endif
@interface NSNumberFormatter (PrivateMethods)
- (void) _openUNumberFormat;
- (void) _closeUNumberFormat;
- (void) _resetUNumberFormat;
- (void) _setSymbol: (NSString *) string : (NSInteger) symbol;
- (NSString *) _getSymbol: (NSInteger) symbol;
- (void) _setTextAttribute: (NSString *) string : (NSInteger) attrib;
@ -302,7 +301,9 @@ static NSUInteger _defaultBehavior = 0;
RELEASE(_attributedStringForNotANumber);
RELEASE(_attributedStringForZero);
RELEASE(_locale);
[self _closeUNumberFormat];
#if GS_USE_ICU == 1
unum_close (_formatter);
#endif
[super dealloc];
}
@ -427,7 +428,7 @@ static NSUInteger _defaultBehavior = 0;
_behavior = _defaultBehavior;
_locale = RETAIN([NSLocale currentLocale]);
[self _openUNumberFormat];
[self _resetUNumberFormat];
return self;
}
@ -711,79 +712,6 @@ static NSUInteger _defaultBehavior = 0;
- (NSString*) stringForObjectValue: (id)anObject
{
if (_behavior == NSNumberFormatterBehavior10_4
|| _behavior == NSNumberFormatterBehaviorDefault)
{
#if GS_USE_ICU == 1
#define STRING_FROM_NUMBER(function, number) do \
{ \
UChar *outStr = buffer; \
UErrorCode err = U_ZERO_ERROR; \
int32_t len; \
NSString *result; \
\
len = function (_formatter, number, outStr, MAX_BUFFER_SIZE, NULL, &err); \
if (len > MAX_BUFFER_SIZE) \
outStr = NSZoneMalloc ([self zone], len * sizeof(UChar));\
err = U_ZERO_ERROR; \
function (_formatter, number, outStr, MAX_BUFFER_SIZE, NULL, &err); \
result = [NSString stringWithCharacters: outStr length: len]; \
if (len > MAX_BUFFER_SIZE) \
NSZoneFree ([self zone], outStr); \
return result; \
} while (0)
// This is quite inefficient. See the GSUText stuff for how
// to use ICU 4.6 UText objects as NSStrings. This saves us from
// needing to do a load of O(n) things. In 4.6, these APIs in ICU
// haven't been updated to use UText (so we have to use the UChar buffer
// approach), but they probably will be in the future. We should
// revisit this code when they have been.
UChar buffer[MAX_BUFFER_SIZE];
// FIXME: What to do with unsigned types?
//
// The only unsigned case we actually need to worry about is unsigned
// long long - all of the others are stored as signed values. We're now
// falling through to the double case for this, which will lose us some
// precision, but hopefully not matter too much...
if (nil == anObject)
return [self nilSymbol];
if (![anObject isKindOfClass: [NSNumber class]])
return [self notANumberSymbol];
switch ([anObject objCType][0])
{
case _C_LNG_LNG:
STRING_FROM_NUMBER(unum_formatInt64, [anObject longLongValue]);
break;
case _C_INT:
STRING_FROM_NUMBER(unum_format, [anObject intValue]);
break;
// Note: This case is probably wrong: the compiler doesn't generate B
// for bool, it generates C or c (depending on the platform). I
// don't think it matters, because we don't bother with anything
// smaller than int for NSNumbers
case _C_BOOL:
STRING_FROM_NUMBER(unum_format, (int)[anObject boolValue]);
break;
// If it's not a type encoding that we recognise, let the receiver
// cast it to a double, which probably has enough precision for what
// we need. This needs testing with NSDecimalNumber though, because
// I managed to break stuff last time I did anything with NSNumber by
// forgetting that NSDecimalNumber existed...
default:
case _C_DBL:
STRING_FROM_NUMBER(unum_formatDouble, [anObject doubleValue]);
break;
case _C_FLT:
STRING_FROM_NUMBER(unum_formatDouble, (double)[anObject floatValue]);
break;
}
#endif
}
else if (_behavior == NSNumberFormatterBehavior10_0)
{
NSMutableDictionary *locale;
NSCharacterSet *formattingCharacters;
NSCharacterSet *placeHolders;
@ -1038,9 +966,6 @@ static NSUInteger _defaultBehavior = 0;
stringByAppendingString: suffix];
[formattedNumber release];
return wholeString;
}
return nil;
}
- (NSDictionary*) textAttributesForNegativeValues
@ -1063,19 +988,127 @@ static NSUInteger _defaultBehavior = 0;
- (NSString *) stringFromNumber: (NSNumber *)number
{
return [self stringForObjectValue: number];
// This is a 10.4 and above method and should not work with earlier version.
#if GS_USE_ICU == 1
#define STRING_FROM_NUMBER(function, number) do \
{ \
UChar *outStr = buffer; \
UErrorCode err = U_ZERO_ERROR; \
int32_t len; \
NSString *result; \
\
len = function (_formatter, number, outStr, MAX_BUFFER_SIZE, NULL, &err); \
if (len > MAX_BUFFER_SIZE) \
outStr = NSZoneMalloc ([self zone], len * sizeof(UChar));\
err = U_ZERO_ERROR; \
function (_formatter, number, outStr, MAX_BUFFER_SIZE, NULL, &err); \
result = [NSString stringWithCharacters: outStr length: len]; \
if (len > MAX_BUFFER_SIZE) \
NSZoneFree ([self zone], outStr); \
return result; \
} while (0)
// This is quite inefficient. See the GSUText stuff for how
// to use ICU 4.6 UText objects as NSStrings. This saves us from
// needing to do a load of O(n) things. In 4.6, these APIs in ICU
// haven't been updated to use UText (so we have to use the UChar buffer
// approach), but they probably will be in the future. We should
// revisit this code when they have been.
UChar buffer[MAX_BUFFER_SIZE];
// FIXME: What to do with unsigned types?
//
// The only unsigned case we actually need to worry about is unsigned
// long long - all of the others are stored as signed values. We're now
// falling through to the double case for this, which will lose us some
// precision, but hopefully not matter too much...
if (nil == number)
return [self nilSymbol];
if (![number isKindOfClass: [NSNumber class]])
return [self notANumberSymbol];
switch ([number objCType][0])
{
case _C_LNG_LNG:
STRING_FROM_NUMBER(unum_formatInt64, [number longLongValue]);
break;
case _C_INT:
STRING_FROM_NUMBER(unum_format, [number intValue]);
break;
// Note: This case is probably wrong: the compiler doesn't generate B
// for bool, it generates C or c (depending on the platform). I
// don't think it matters, because we don't bother with anything
// smaller than int for NSNumbers
case _C_BOOL:
STRING_FROM_NUMBER(unum_format, (int)[number boolValue]);
break;
// If it's not a type encoding that we recognise, let the receiver
// cast it to a double, which probably has enough precision for what
// we need. This needs testing with NSDecimalNumber though, because
// I managed to break stuff last time I did anything with NSNumber by
// forgetting that NSDecimalNumber existed...
default:
case _C_DBL:
STRING_FROM_NUMBER(unum_formatDouble, [number doubleValue]);
break;
case _C_FLT:
STRING_FROM_NUMBER(unum_formatDouble, (double)[number floatValue]);
break;
}
#else
return nil;
#endif
}
- (NSNumber *) numberFromString: (NSString *)string
{
id number = nil;
NSString *error;
// This is a 10.4 and above method and should not work with earlier version.
#if GS_USE_ICU == 1
NSNumber *result;
NSUInteger length;
NSRange range;
UErrorCode err = U_ZERO_ERROR;
unichar *ustring;
int64_t intNum;
double doubleNum;
[self getObjectValue: &number
forString: string
errorDescription: &error];
if (string == nil)
return nil;
return number;
length = [string length];
ustring = NSZoneMalloc ([self zone], sizeof(unichar) * length);
if (ustring == NULL)
return nil;
[string getCharacters: ustring range: NSMakeRange(0, length)];
// FIXME: Not sure if this is correct....
range = [string rangeOfString: @"."];
if (range.location == NSNotFound)
{
intNum = unum_parseInt64 (_formatter, ustring, length, NULL, &err);
if (U_FAILURE(err))
return nil;
if (intNum == 0 || intNum == 1)
result = [NSNumber numberWithBool: (BOOL) intNum];
else if (intNum < INT_MAX && intNum > INT_MIN)
result = [NSNumber numberWithInt: (int32_t)intNum];
else
result = [NSNumber numberWithLongLong: intNum];
}
else
{
doubleNum = unum_parseDouble (_formatter, ustring, length, NULL, &err);
if (U_FAILURE(err))
return nil;
result = [NSNumber numberWithDouble: doubleNum];
}
NSZoneFree ([self zone], ustring);
return result;
#else
return nil;
#endif
}
@ -1102,9 +1135,8 @@ static NSUInteger _defaultBehavior = 0;
- (void) setNumberStyle: (NSNumberFormatterStyle) style
{
[self _closeUNumberFormat];
_style = style;
[self _openUNumberFormat];
[self _resetUNumberFormat];
}
- (NSNumberFormatterStyle) numberStyle
@ -1126,13 +1158,12 @@ static NSUInteger _defaultBehavior = 0;
- (void) setLocale: (NSLocale *) locale
{
RELEASE(_locale);
[self _closeUNumberFormat];
if (locale == nil)
locale = [NSLocale currentLocale];
_locale = RETAIN(locale);
[self _openUNumberFormat];
[self _resetUNumberFormat];
}
- (NSLocale *) locale
@ -1204,8 +1235,8 @@ static NSUInteger _defaultBehavior = 0;
- (void) setMultiplier: (NSNumber *) number
{
#if GS_USE_ICU == 1
double value = [number doubleValue];
unum_setDoubleAttribute (_formatter, UNUM_MULTIPLIER, value);
int32_t value = [number intValue];
unum_setAttribute (_formatter, UNUM_MULTIPLIER, value);
#else
return;
#endif
@ -1214,8 +1245,8 @@ static NSUInteger _defaultBehavior = 0;
- (NSNumber *) multiplier
{
#if GS_USE_ICU == 1
double value = unum_getDoubleAttribute (_formatter, UNUM_MULTIPLIER);
return [NSNumber numberWithDouble: value];
int32_t value = unum_getAttribute (_formatter, UNUM_MULTIPLIER);
return [NSNumber numberWithInt: value];
#else
return nil;
#endif
@ -1934,13 +1965,16 @@ static NSUInteger _defaultBehavior = 0;
@end
@implementation NSNumberFormatter (PrivateMethods)
- (void) _openUNumberFormat
- (void) _resetUNumberFormat
{
#if GS_USE_ICU == 1
UNumberFormatStyle style;
UErrorCode err = U_ZERO_ERROR;
const char *cLocaleId;
if (_formatter)
unum_close(_formatter);
cLocaleId = [[_locale localeIdentifier] UTF8String];
style = _NSToICUFormatStyle (_style);
@ -1952,15 +1986,6 @@ static NSUInteger _defaultBehavior = 0;
#endif
}
- (void) _closeUNumberFormat
{
#if GS_USE_ICU == 1
unum_close (_formatter);
#else
return;
#endif
}
- (void) _setSymbol: (NSString *) string : (NSInteger) symbol
{
#if GS_USE_ICU == 1