mirror of
https://github.com/gnustep/libs-base.git
synced 2025-06-02 09:31:07 +00:00
Fixed errors found by test suite.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@31967 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
b1ce49ff43
commit
86b566f237
2 changed files with 324 additions and 314 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
2011-01-28 Stefan Bidigaray <stefanbidi@gmail.com>
|
||||||
|
|
||||||
|
* Source/NSNumberFormatter.m: Fixed errors found by test suite.
|
||||||
|
|
||||||
2011-01-27 Stefan Bidigaray <stefanbidi@gmail.com>
|
2011-01-27 Stefan Bidigaray <stefanbidi@gmail.com>
|
||||||
|
|
||||||
* Source/NSCalendar.m: Add -isEqual:.
|
* Source/NSCalendar.m: Add -isEqual:.
|
||||||
|
|
|
@ -428,7 +428,7 @@ static NSUInteger _defaultBehavior = 0;
|
||||||
|
|
||||||
_behavior = _defaultBehavior;
|
_behavior = _defaultBehavior;
|
||||||
_locale = RETAIN([NSLocale currentLocale]);
|
_locale = RETAIN([NSLocale currentLocale]);
|
||||||
_style = NSNumberFormatterDecimalStyle;
|
_style = NSNumberFormatterNoStyle;
|
||||||
[self _resetUNumberFormat];
|
[self _resetUNumberFormat];
|
||||||
if (_formatter == NULL)
|
if (_formatter == NULL)
|
||||||
{
|
{
|
||||||
|
@ -436,9 +436,6 @@ static NSUInteger _defaultBehavior = 0;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default values...
|
|
||||||
[self setMaximumFractionDigits: 2];
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,260 +718,335 @@ static NSUInteger _defaultBehavior = 0;
|
||||||
|
|
||||||
- (NSString*) stringForObjectValue: (id)anObject
|
- (NSString*) stringForObjectValue: (id)anObject
|
||||||
{
|
{
|
||||||
NSMutableDictionary *locale;
|
if (_behavior == NSNumberFormatterBehaviorDefault
|
||||||
NSCharacterSet *formattingCharacters;
|
|| _behavior == NSNumberFormatterBehavior10_4)
|
||||||
NSCharacterSet *placeHolders;
|
{
|
||||||
NSString *prefix;
|
#if GS_USE_ICU == 1
|
||||||
NSString *suffix;
|
|
||||||
NSString *wholeString;
|
|
||||||
NSString *fracPad = nil;
|
|
||||||
NSString *fracPartString;
|
|
||||||
NSMutableString *intPartString;
|
|
||||||
NSMutableString *formattedNumber;
|
|
||||||
NSMutableString *intPad;
|
|
||||||
NSRange prefixRange;
|
|
||||||
NSRange decimalPlaceRange;
|
|
||||||
NSRange suffixRange;
|
|
||||||
NSRange intPartRange;
|
|
||||||
NSDecimal representativeDecimal;
|
|
||||||
NSDecimal roundedDecimal;
|
|
||||||
NSDecimalNumber *roundedNumber;
|
|
||||||
NSDecimalNumber *intPart;
|
|
||||||
NSDecimalNumber *fracPart;
|
|
||||||
int decimalPlaces = 0;
|
|
||||||
BOOL displayThousandsSeparators = NO;
|
|
||||||
BOOL displayFractionalPart = NO;
|
|
||||||
BOOL negativeNumber = NO;
|
|
||||||
NSString *useFormat;
|
|
||||||
NSString *defaultDecimalSeparator = nil;
|
|
||||||
NSString *defaultThousandsSeparator = nil;
|
|
||||||
|
|
||||||
if (_localizesFormat)
|
#define STRING_FROM_NUMBER(function, number) do \
|
||||||
{
|
{ \
|
||||||
NSDictionary *defaultLocale = GSDomainFromDefaultLocale();
|
UChar *outStr = buffer; \
|
||||||
defaultDecimalSeparator
|
UErrorCode err = U_ZERO_ERROR; \
|
||||||
= [defaultLocale objectForKey: NSDecimalSeparator];
|
int32_t len; \
|
||||||
defaultThousandsSeparator
|
NSString *result; \
|
||||||
= [defaultLocale objectForKey: NSThousandsSeparator];
|
\
|
||||||
}
|
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)
|
||||||
|
|
||||||
if (defaultDecimalSeparator == nil)
|
// This is quite inefficient. See the GSUText stuff for how
|
||||||
{
|
// to use ICU 4.6 UText objects as NSStrings. This saves us from
|
||||||
defaultDecimalSeparator = @".";
|
// 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
|
||||||
if (defaultThousandsSeparator == nil)
|
// approach), but they probably will be in the future. We should
|
||||||
{
|
// revisit this code when they have been.
|
||||||
defaultThousandsSeparator = @",";
|
UChar buffer[MAX_BUFFER_SIZE];
|
||||||
}
|
|
||||||
formattingCharacters = [NSCharacterSet
|
// FIXME: What to do with unsigned types?
|
||||||
characterSetWithCharactersInString: @"0123456789#.,_"];
|
//
|
||||||
placeHolders = [NSCharacterSet
|
// The only unsigned case we actually need to worry about is unsigned
|
||||||
characterSetWithCharactersInString: @"0123456789#_"];
|
// 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
|
||||||
if (nil == anObject)
|
// precision, but hopefully not matter too much...
|
||||||
return [[self attributedStringForNil] string];
|
if (nil == anObject)
|
||||||
if (![anObject isKindOfClass: [NSNumber class]])
|
return [self nilSymbol];
|
||||||
return [[self attributedStringForNotANumber] string];
|
if (![anObject isKindOfClass: [NSNumber class]])
|
||||||
if ([anObject isEqual: [NSDecimalNumber notANumber]])
|
return [self notANumberSymbol];
|
||||||
return [[self attributedStringForNotANumber] string];
|
switch ([anObject objCType][0])
|
||||||
if (_attributedStringForZero
|
|
||||||
&& [anObject isEqual: [NSDecimalNumber zero]])
|
|
||||||
return [[self attributedStringForZero] string];
|
|
||||||
|
|
||||||
useFormat = _positiveFormat;
|
|
||||||
if ([(NSNumber*)anObject compare: [NSDecimalNumber zero]]
|
|
||||||
== NSOrderedAscending)
|
|
||||||
{
|
|
||||||
useFormat = _negativeFormat;
|
|
||||||
negativeNumber = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no format specified, use the same default that Cocoa does
|
|
||||||
if (nil == useFormat)
|
|
||||||
{
|
|
||||||
useFormat = [NSString stringWithFormat: @"%@#%@###%@##",
|
|
||||||
negativeNumber ? @"-" : @"",
|
|
||||||
defaultThousandsSeparator,
|
|
||||||
defaultDecimalSeparator];
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixRange = [useFormat rangeOfCharacterFromSet: formattingCharacters];
|
|
||||||
if (NSNotFound != prefixRange.location)
|
|
||||||
{
|
|
||||||
prefix = [useFormat substringToIndex: prefixRange.location];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
prefix = @"";
|
|
||||||
}
|
|
||||||
|
|
||||||
locale = [NSMutableDictionary dictionaryWithCapacity: 3];
|
|
||||||
[locale setObject: @"" forKey: NSThousandsSeparator];
|
|
||||||
[locale setObject: @"" forKey: NSDecimalSeparator];
|
|
||||||
|
|
||||||
//should also set NSDecimalDigits?
|
|
||||||
|
|
||||||
if ([self hasThousandSeparators]
|
|
||||||
&& (0 != [useFormat rangeOfString: defaultThousandsSeparator].length))
|
|
||||||
{
|
|
||||||
displayThousandsSeparators = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([self allowsFloats]
|
|
||||||
&& (NSNotFound
|
|
||||||
!= [useFormat rangeOfString: defaultDecimalSeparator].location))
|
|
||||||
{
|
|
||||||
decimalPlaceRange = [useFormat rangeOfString: defaultDecimalSeparator
|
|
||||||
options: NSBackwardsSearch];
|
|
||||||
if (NSMaxRange(decimalPlaceRange) == [useFormat length])
|
|
||||||
{
|
{
|
||||||
decimalPlaces = 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;
|
||||||
|
NSString *prefix;
|
||||||
|
NSString *suffix;
|
||||||
|
NSString *wholeString;
|
||||||
|
NSString *fracPad = nil;
|
||||||
|
NSString *fracPartString;
|
||||||
|
NSMutableString *intPartString;
|
||||||
|
NSMutableString *formattedNumber;
|
||||||
|
NSMutableString *intPad;
|
||||||
|
NSRange prefixRange;
|
||||||
|
NSRange decimalPlaceRange;
|
||||||
|
NSRange suffixRange;
|
||||||
|
NSRange intPartRange;
|
||||||
|
NSDecimal representativeDecimal;
|
||||||
|
NSDecimal roundedDecimal;
|
||||||
|
NSDecimalNumber *roundedNumber;
|
||||||
|
NSDecimalNumber *intPart;
|
||||||
|
NSDecimalNumber *fracPart;
|
||||||
|
int decimalPlaces = 0;
|
||||||
|
BOOL displayThousandsSeparators = NO;
|
||||||
|
BOOL displayFractionalPart = NO;
|
||||||
|
BOOL negativeNumber = NO;
|
||||||
|
NSString *useFormat;
|
||||||
|
NSString *defaultDecimalSeparator = nil;
|
||||||
|
NSString *defaultThousandsSeparator = nil;
|
||||||
|
|
||||||
|
if (_localizesFormat)
|
||||||
|
{
|
||||||
|
NSDictionary *defaultLocale = GSDomainFromDefaultLocale();
|
||||||
|
defaultDecimalSeparator
|
||||||
|
= [defaultLocale objectForKey: NSDecimalSeparator];
|
||||||
|
defaultThousandsSeparator
|
||||||
|
= [defaultLocale objectForKey: NSThousandsSeparator];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultDecimalSeparator == nil)
|
||||||
|
{
|
||||||
|
defaultDecimalSeparator = @".";
|
||||||
|
}
|
||||||
|
if (defaultThousandsSeparator == nil)
|
||||||
|
{
|
||||||
|
defaultThousandsSeparator = @",";
|
||||||
|
}
|
||||||
|
formattingCharacters = [NSCharacterSet
|
||||||
|
characterSetWithCharactersInString: @"0123456789#.,_"];
|
||||||
|
placeHolders = [NSCharacterSet
|
||||||
|
characterSetWithCharactersInString: @"0123456789#_"];
|
||||||
|
|
||||||
|
if (nil == anObject)
|
||||||
|
return [[self attributedStringForNil] string];
|
||||||
|
if (![anObject isKindOfClass: [NSNumber class]])
|
||||||
|
return [[self attributedStringForNotANumber] string];
|
||||||
|
if ([anObject isEqual: [NSDecimalNumber notANumber]])
|
||||||
|
return [[self attributedStringForNotANumber] string];
|
||||||
|
if (_attributedStringForZero
|
||||||
|
&& [anObject isEqual: [NSDecimalNumber zero]])
|
||||||
|
return [[self attributedStringForZero] string];
|
||||||
|
|
||||||
|
useFormat = _positiveFormat;
|
||||||
|
if ([(NSNumber*)anObject compare: [NSDecimalNumber zero]]
|
||||||
|
== NSOrderedAscending)
|
||||||
|
{
|
||||||
|
useFormat = _negativeFormat;
|
||||||
|
negativeNumber = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no format specified, use the same default that Cocoa does
|
||||||
|
if (nil == useFormat)
|
||||||
|
{
|
||||||
|
useFormat = [NSString stringWithFormat: @"%@#%@###%@##",
|
||||||
|
negativeNumber ? @"-" : @"",
|
||||||
|
defaultThousandsSeparator,
|
||||||
|
defaultDecimalSeparator];
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixRange = [useFormat rangeOfCharacterFromSet: formattingCharacters];
|
||||||
|
if (NSNotFound != prefixRange.location)
|
||||||
|
{
|
||||||
|
prefix = [useFormat substringToIndex: prefixRange.location];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while ([placeHolders characterIsMember:
|
prefix = @"";
|
||||||
[useFormat characterAtIndex: NSMaxRange(decimalPlaceRange)]])
|
}
|
||||||
|
|
||||||
|
locale = [NSMutableDictionary dictionaryWithCapacity: 3];
|
||||||
|
[locale setObject: @"" forKey: NSThousandsSeparator];
|
||||||
|
[locale setObject: @"" forKey: NSDecimalSeparator];
|
||||||
|
|
||||||
|
//should also set NSDecimalDigits?
|
||||||
|
|
||||||
|
if ([self hasThousandSeparators]
|
||||||
|
&& (0 != [useFormat rangeOfString: defaultThousandsSeparator].length))
|
||||||
|
{
|
||||||
|
displayThousandsSeparators = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self allowsFloats]
|
||||||
|
&& (NSNotFound
|
||||||
|
!= [useFormat rangeOfString: defaultDecimalSeparator].location))
|
||||||
|
{
|
||||||
|
decimalPlaceRange = [useFormat rangeOfString: defaultDecimalSeparator
|
||||||
|
options: NSBackwardsSearch];
|
||||||
|
if (NSMaxRange(decimalPlaceRange) == [useFormat length])
|
||||||
{
|
{
|
||||||
decimalPlaceRange.length++;
|
decimalPlaces = 0;
|
||||||
if (NSMaxRange(decimalPlaceRange) == [useFormat length])
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
decimalPlaces=decimalPlaceRange.length -= 1;
|
else
|
||||||
decimalPlaceRange.location += 1;
|
|
||||||
fracPad = [useFormat substringWithRange:decimalPlaceRange];
|
|
||||||
}
|
|
||||||
if (0 != decimalPlaces)
|
|
||||||
displayFractionalPart = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
representativeDecimal = [anObject decimalValue];
|
|
||||||
NSDecimalRound(&roundedDecimal, &representativeDecimal, decimalPlaces,
|
|
||||||
NSRoundPlain);
|
|
||||||
roundedNumber =
|
|
||||||
[NSDecimalNumber decimalNumberWithDecimal: roundedDecimal];
|
|
||||||
|
|
||||||
/* Arguably this fiddling could be done by GSDecimalString() but I
|
|
||||||
* thought better to leave that behaviour as it is and provide the
|
|
||||||
* desired prettification here
|
|
||||||
*/
|
|
||||||
if (negativeNumber)
|
|
||||||
roundedNumber = [roundedNumber decimalNumberByMultiplyingBy:
|
|
||||||
(NSDecimalNumber*)[NSDecimalNumber numberWithInt: -1]];
|
|
||||||
intPart = (NSDecimalNumber*)
|
|
||||||
[NSDecimalNumber numberWithInt: (int)[roundedNumber doubleValue]];
|
|
||||||
fracPart = [roundedNumber decimalNumberBySubtracting: intPart];
|
|
||||||
intPartString
|
|
||||||
= AUTORELEASE([[intPart descriptionWithLocale: locale] mutableCopy]);
|
|
||||||
|
|
||||||
//sort out the padding for the integer part
|
|
||||||
intPartRange = [useFormat rangeOfCharacterFromSet: placeHolders];
|
|
||||||
if (NSMaxRange(intPartRange) < ([useFormat length] - 1))
|
|
||||||
{
|
|
||||||
while (([placeHolders characterIsMember:
|
|
||||||
[useFormat characterAtIndex: NSMaxRange(intPartRange)]]
|
|
||||||
|| [[useFormat substringFromRange:
|
|
||||||
NSMakeRange(NSMaxRange(intPartRange), 1)] isEqual:
|
|
||||||
defaultThousandsSeparator])
|
|
||||||
&& NSMaxRange(intPartRange) < [useFormat length] - 1)
|
|
||||||
{
|
|
||||||
intPartRange.length++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
intPad = [[useFormat substringWithRange: intPartRange] mutableCopy];
|
|
||||||
[intPad replaceOccurrencesOfString: defaultThousandsSeparator
|
|
||||||
withString: @""
|
|
||||||
options: 0
|
|
||||||
range: NSMakeRange(0, [intPad length])];
|
|
||||||
[intPad replaceOccurrencesOfString: @"#"
|
|
||||||
withString: @""
|
|
||||||
options: NSAnchoredSearch
|
|
||||||
range: NSMakeRange(0, [intPad length])];
|
|
||||||
if ([intPad length] > [intPartString length])
|
|
||||||
{
|
|
||||||
NSRange ipRange;
|
|
||||||
|
|
||||||
ipRange =
|
|
||||||
NSMakeRange(0, [intPad length] - [intPartString length] + 1);
|
|
||||||
[intPartString insertString:
|
|
||||||
[intPad substringWithRange: ipRange] atIndex: 0];
|
|
||||||
[intPartString replaceOccurrencesOfString: @"_"
|
|
||||||
withString: @" "
|
|
||||||
options: 0
|
|
||||||
range: NSMakeRange(0, [intPartString length])];
|
|
||||||
[intPartString replaceOccurrencesOfString: @"#"
|
|
||||||
withString: @"0"
|
|
||||||
options: 0
|
|
||||||
range: NSMakeRange(0, [intPartString length])];
|
|
||||||
}
|
|
||||||
// fix the thousands separators up
|
|
||||||
if (displayThousandsSeparators && [intPartString length] > 3)
|
|
||||||
{
|
|
||||||
int index = [intPartString length];
|
|
||||||
|
|
||||||
while (0 < (index -= 3))
|
|
||||||
{
|
|
||||||
[intPartString insertString: [self thousandSeparator] atIndex: index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
formattedNumber = [intPartString mutableCopy];
|
|
||||||
|
|
||||||
//fix up the fractional part
|
|
||||||
if (displayFractionalPart)
|
|
||||||
{
|
|
||||||
if (0 != decimalPlaces)
|
|
||||||
{
|
|
||||||
NSMutableString *ms;
|
|
||||||
|
|
||||||
fracPart = [fracPart decimalNumberByMultiplyingByPowerOf10:
|
|
||||||
decimalPlaces];
|
|
||||||
ms = [[fracPart descriptionWithLocale: locale] mutableCopy];
|
|
||||||
[ms replaceOccurrencesOfString: @"0"
|
|
||||||
withString: @""
|
|
||||||
options: (NSBackwardsSearch | NSAnchoredSearch)
|
|
||||||
range: NSMakeRange(0, [ms length])];
|
|
||||||
if ([fracPad length] > [ms length])
|
|
||||||
{
|
{
|
||||||
NSRange fpRange;
|
while ([placeHolders characterIsMember:
|
||||||
|
[useFormat characterAtIndex: NSMaxRange(decimalPlaceRange)]])
|
||||||
|
{
|
||||||
|
decimalPlaceRange.length++;
|
||||||
|
if (NSMaxRange(decimalPlaceRange) == [useFormat length])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
decimalPlaces=decimalPlaceRange.length -= 1;
|
||||||
|
decimalPlaceRange.location += 1;
|
||||||
|
fracPad = [useFormat substringWithRange:decimalPlaceRange];
|
||||||
|
}
|
||||||
|
if (0 != decimalPlaces)
|
||||||
|
displayFractionalPart = YES;
|
||||||
|
}
|
||||||
|
|
||||||
fpRange = NSMakeRange([ms length],
|
representativeDecimal = [anObject decimalValue];
|
||||||
([fracPad length] - [ms length]));
|
NSDecimalRound(&roundedDecimal, &representativeDecimal, decimalPlaces,
|
||||||
[ms appendString:
|
NSRoundPlain);
|
||||||
[fracPad substringWithRange: fpRange]];
|
roundedNumber =
|
||||||
[ms replaceOccurrencesOfString: @"#"
|
[NSDecimalNumber decimalNumberWithDecimal: roundedDecimal];
|
||||||
withString: @""
|
|
||||||
options: (NSBackwardsSearch | NSAnchoredSearch)
|
/* Arguably this fiddling could be done by GSDecimalString() but I
|
||||||
range: NSMakeRange(0, [ms length])];
|
* thought better to leave that behaviour as it is and provide the
|
||||||
[ms replaceOccurrencesOfString: @"#"
|
* desired prettification here
|
||||||
withString: @"0"
|
*/
|
||||||
options: 0
|
if (negativeNumber)
|
||||||
range: NSMakeRange(0, [ms length])];
|
roundedNumber = [roundedNumber decimalNumberByMultiplyingBy:
|
||||||
[ms replaceOccurrencesOfString: @"_"
|
(NSDecimalNumber*)[NSDecimalNumber numberWithInt: -1]];
|
||||||
withString: @" "
|
intPart = (NSDecimalNumber*)
|
||||||
options: 0
|
[NSDecimalNumber numberWithInt: (int)[roundedNumber doubleValue]];
|
||||||
range: NSMakeRange(0, [ms length])];
|
fracPart = [roundedNumber decimalNumberBySubtracting: intPart];
|
||||||
}
|
intPartString
|
||||||
fracPartString = AUTORELEASE(ms);
|
= AUTORELEASE([[intPart descriptionWithLocale: locale] mutableCopy]);
|
||||||
}
|
|
||||||
else
|
//sort out the padding for the integer part
|
||||||
|
intPartRange = [useFormat rangeOfCharacterFromSet: placeHolders];
|
||||||
|
if (NSMaxRange(intPartRange) < ([useFormat length] - 1))
|
||||||
{
|
{
|
||||||
fracPartString = @"0";
|
while (([placeHolders characterIsMember:
|
||||||
|
[useFormat characterAtIndex: NSMaxRange(intPartRange)]]
|
||||||
|
|| [[useFormat substringFromRange:
|
||||||
|
NSMakeRange(NSMaxRange(intPartRange), 1)] isEqual:
|
||||||
|
defaultThousandsSeparator])
|
||||||
|
&& NSMaxRange(intPartRange) < [useFormat length] - 1)
|
||||||
|
{
|
||||||
|
intPartRange.length++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
[formattedNumber appendString: [self decimalSeparator]];
|
intPad = [[useFormat substringWithRange: intPartRange] mutableCopy];
|
||||||
[formattedNumber appendString: fracPartString];
|
[intPad replaceOccurrencesOfString: defaultThousandsSeparator
|
||||||
|
withString: @""
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [intPad length])];
|
||||||
|
[intPad replaceOccurrencesOfString: @"#"
|
||||||
|
withString: @""
|
||||||
|
options: NSAnchoredSearch
|
||||||
|
range: NSMakeRange(0, [intPad length])];
|
||||||
|
if ([intPad length] > [intPartString length])
|
||||||
|
{
|
||||||
|
NSRange ipRange;
|
||||||
|
|
||||||
|
ipRange =
|
||||||
|
NSMakeRange(0, [intPad length] - [intPartString length] + 1);
|
||||||
|
[intPartString insertString:
|
||||||
|
[intPad substringWithRange: ipRange] atIndex: 0];
|
||||||
|
[intPartString replaceOccurrencesOfString: @"_"
|
||||||
|
withString: @" "
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [intPartString length])];
|
||||||
|
[intPartString replaceOccurrencesOfString: @"#"
|
||||||
|
withString: @"0"
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [intPartString length])];
|
||||||
|
}
|
||||||
|
// fix the thousands separators up
|
||||||
|
if (displayThousandsSeparators && [intPartString length] > 3)
|
||||||
|
{
|
||||||
|
int index = [intPartString length];
|
||||||
|
|
||||||
|
while (0 < (index -= 3))
|
||||||
|
{
|
||||||
|
[intPartString insertString: [self thousandSeparator] atIndex: index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedNumber = [intPartString mutableCopy];
|
||||||
|
|
||||||
|
//fix up the fractional part
|
||||||
|
if (displayFractionalPart)
|
||||||
|
{
|
||||||
|
if (0 != decimalPlaces)
|
||||||
|
{
|
||||||
|
NSMutableString *ms;
|
||||||
|
|
||||||
|
fracPart = [fracPart decimalNumberByMultiplyingByPowerOf10:
|
||||||
|
decimalPlaces];
|
||||||
|
ms = [[fracPart descriptionWithLocale: locale] mutableCopy];
|
||||||
|
[ms replaceOccurrencesOfString: @"0"
|
||||||
|
withString: @""
|
||||||
|
options: (NSBackwardsSearch | NSAnchoredSearch)
|
||||||
|
range: NSMakeRange(0, [ms length])];
|
||||||
|
if ([fracPad length] > [ms length])
|
||||||
|
{
|
||||||
|
NSRange fpRange;
|
||||||
|
|
||||||
|
fpRange = NSMakeRange([ms length],
|
||||||
|
([fracPad length] - [ms length]));
|
||||||
|
[ms appendString:
|
||||||
|
[fracPad substringWithRange: fpRange]];
|
||||||
|
[ms replaceOccurrencesOfString: @"#"
|
||||||
|
withString: @""
|
||||||
|
options: (NSBackwardsSearch | NSAnchoredSearch)
|
||||||
|
range: NSMakeRange(0, [ms length])];
|
||||||
|
[ms replaceOccurrencesOfString: @"#"
|
||||||
|
withString: @"0"
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [ms length])];
|
||||||
|
[ms replaceOccurrencesOfString: @"_"
|
||||||
|
withString: @" "
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [ms length])];
|
||||||
|
}
|
||||||
|
fracPartString = AUTORELEASE(ms);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fracPartString = @"0";
|
||||||
|
}
|
||||||
|
[formattedNumber appendString: [self decimalSeparator]];
|
||||||
|
[formattedNumber appendString: fracPartString];
|
||||||
|
}
|
||||||
|
/*FIXME - the suffix doesn't behave the same as on Mac OS X.
|
||||||
|
* Our suffix is everything which follows the final formatting
|
||||||
|
* character. Cocoa's suffix is everything which isn't a
|
||||||
|
* formatting character nor in the prefix
|
||||||
|
*/
|
||||||
|
suffixRange = [useFormat rangeOfCharacterFromSet: formattingCharacters
|
||||||
|
options: NSBackwardsSearch];
|
||||||
|
suffix = [useFormat substringFromIndex: NSMaxRange(suffixRange)];
|
||||||
|
wholeString = [[prefix stringByAppendingString: formattedNumber]
|
||||||
|
stringByAppendingString: suffix];
|
||||||
|
[formattedNumber release];
|
||||||
|
return wholeString;
|
||||||
}
|
}
|
||||||
/*FIXME - the suffix doesn't behave the same as on Mac OS X.
|
return nil;
|
||||||
* Our suffix is everything which follows the final formatting
|
|
||||||
* character. Cocoa's suffix is everything which isn't a
|
|
||||||
* formatting character nor in the prefix
|
|
||||||
*/
|
|
||||||
suffixRange = [useFormat rangeOfCharacterFromSet: formattingCharacters
|
|
||||||
options: NSBackwardsSearch];
|
|
||||||
suffix = [useFormat substringFromIndex: NSMaxRange(suffixRange)];
|
|
||||||
wholeString = [[prefix stringByAppendingString: formattedNumber]
|
|
||||||
stringByAppendingString: suffix];
|
|
||||||
[formattedNumber release];
|
|
||||||
return wholeString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary*) textAttributesForNegativeValues
|
- (NSDictionary*) textAttributesForNegativeValues
|
||||||
|
@ -998,75 +1070,7 @@ static NSUInteger _defaultBehavior = 0;
|
||||||
- (NSString *) stringFromNumber: (NSNumber *)number
|
- (NSString *) stringFromNumber: (NSNumber *)number
|
||||||
{
|
{
|
||||||
// This is a 10.4 and above method and should not work with earlier version.
|
// This is a 10.4 and above method and should not work with earlier version.
|
||||||
#if GS_USE_ICU == 1
|
return [self stringForObjectValue: number];
|
||||||
|
|
||||||
#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
|
- (NSNumber *) numberFromString: (NSString *)string
|
||||||
|
@ -1990,6 +1994,8 @@ static NSUInteger _defaultBehavior = 0;
|
||||||
_formatter = unum_open (style, NULL, 0, cLocaleId, NULL, &err);
|
_formatter = unum_open (style, NULL, 0, cLocaleId, NULL, &err);
|
||||||
if (U_FAILURE(err))
|
if (U_FAILURE(err))
|
||||||
_formatter = NULL;
|
_formatter = NULL;
|
||||||
|
|
||||||
|
[self setMaximumFractionDigits: 0];
|
||||||
#else
|
#else
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue