mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-11-27 05:50:27 +00:00
1769 lines
52 KiB
Objective-C
1769 lines
52 KiB
Objective-C
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd <cjwl@objc.net>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSAttributedString.h>
|
|
#import <Foundation/NSBundle.h>
|
|
#import <Foundation/NSCharacterSet.h>
|
|
#import <Foundation/NSCoder.h>
|
|
#import <Foundation/NSDictionary.h>
|
|
#import <Foundation/NSError.h>
|
|
#import <Foundation/NSException.h>
|
|
#import <Foundation/NSLocale.h>
|
|
#import <Foundation/NSNumber.h>
|
|
#import <Foundation/NSNumberFormatter.h>
|
|
#import <Foundation/NSRaise.h>
|
|
#import <Foundation/NSScanner.h>
|
|
#import <Foundation/NSString.h>
|
|
#import <Foundation/NSStringFormatter.h>
|
|
#import <Foundation/NSString_unicodePtr.h>
|
|
|
|
#define NSNumberFormatterThousandSeparator ','
|
|
#define NSNumberFormatterDecimalSeparator '.'
|
|
#define NSNumberFormatterPlaceholder '#'
|
|
#define NSNumberFormatterSpace '_'
|
|
#define NSNumberFormatterCurrency '$'
|
|
|
|
@implementation NSNumberFormatter
|
|
|
|
static NSNumberFormatterBehavior _defaultFormatterBehavior =
|
|
NSNumberFormatterBehavior10_4;
|
|
|
|
+ (NSNumberFormatterBehavior) defaultFormatterBehavior {
|
|
return _defaultFormatterBehavior;
|
|
}
|
|
|
|
+ (void) setDefaultFormatterBehavior: (NSNumberFormatterBehavior) value {
|
|
if (value == NSNumberFormatterBehaviorDefault)
|
|
_defaultFormatterBehavior = NSNumberFormatterBehavior10_4;
|
|
else
|
|
_defaultFormatterBehavior = value;
|
|
}
|
|
|
|
- init {
|
|
[super init];
|
|
_behavior = _defaultFormatterBehavior;
|
|
_numberStyle = NSNumberFormatterNoStyle;
|
|
|
|
_locale = [[NSLocale currentLocale] retain];
|
|
|
|
_thousandSeparator =
|
|
[[_locale objectForKey: NSLocaleGroupingSeparator] retain];
|
|
_decimalSeparator =
|
|
[[_locale objectForKey: NSLocaleDecimalSeparator] retain];
|
|
_attributedStringForNil = [[NSAttributedString allocWithZone: NULL]
|
|
initWithString: @"(null)"];
|
|
_attributedStringForNotANumber =
|
|
[[NSAttributedString allocWithZone: NULL] initWithString: @"NaN"];
|
|
_attributedStringForZero =
|
|
[[NSAttributedString allocWithZone: NULL] initWithString: @"0.0"];
|
|
_allowsFloats = YES;
|
|
|
|
return self;
|
|
}
|
|
|
|
// FIXME: doesnt do everything
|
|
|
|
/*
|
|
* The format string (on 10.4 - which Cocotron currently supports) follows this
|
|
* standard: http://unicode.org/reports/tr35/tr35-4.html#Number_Format_Patterns
|
|
* The parser isn't handling every option in the string but at least the common
|
|
* things such as: [prefix] #,##0.### [suffix]
|
|
*
|
|
*/
|
|
|
|
static void extractFormat(NSString *format, NSString **prefix,
|
|
NSString **suffix, NSUInteger *minimumIntegerDigitsp,
|
|
NSUInteger *maximumIntegerDigitsp,
|
|
NSUInteger *minimumFractionDigitsp,
|
|
NSUInteger *maximumFractionDigitsp,
|
|
NSUInteger *groupingSizep,
|
|
NSUInteger *secondaryGroupingSizep)
|
|
{
|
|
NSUInteger length = [format length];
|
|
NSUInteger prefixLength = 0;
|
|
NSUInteger suffixLength = 0;
|
|
|
|
NSUInteger groupingSize = 0;
|
|
NSUInteger secondaryGroupingSize = 0;
|
|
|
|
unichar buffer[length];
|
|
unichar prefixBuffer[length], suffixBuffer[length];
|
|
|
|
enum {
|
|
STATE_PREFIX,
|
|
STATE_INTEGER,
|
|
STATE_FRACTION,
|
|
STATE_SUFFIX,
|
|
} state = STATE_PREFIX;
|
|
|
|
NSUInteger minimumIntegerDigits, maximumIntegerDigits,
|
|
minimumFractionDigits, maximumFractionDigits;
|
|
minimumIntegerDigits = 0;
|
|
maximumIntegerDigits = 0;
|
|
minimumFractionDigits = 0;
|
|
maximumFractionDigits = 0;
|
|
|
|
BOOL foundPrimaryGrouping = NO;
|
|
|
|
[format getCharacters: buffer];
|
|
|
|
NSInteger i = 0;
|
|
for (i = 0; i < length; i++) {
|
|
unichar code = buffer[i];
|
|
|
|
switch (state) {
|
|
|
|
case STATE_PREFIX:
|
|
// Looking for non-numeric chars leading off the format - stop when
|
|
// we find a 0 or a # or a '.'
|
|
if (code == '.') {
|
|
state = STATE_FRACTION;
|
|
} else if (code == '#' ||
|
|
code == '0') { // starting off with a hash or a 0
|
|
state = STATE_INTEGER;
|
|
i--; // step back so we can process these chars in the right
|
|
// state
|
|
} else {
|
|
// Suck up chars into the prefix
|
|
prefixBuffer[prefixLength++] = code;
|
|
}
|
|
break;
|
|
case STATE_INTEGER:
|
|
if (code == '.') {
|
|
state = STATE_FRACTION;
|
|
// No need to step back - the '.' just marks the separation
|
|
} else if (code == '#') {
|
|
if (foundPrimaryGrouping) {
|
|
groupingSize++;
|
|
}
|
|
maximumIntegerDigits++;
|
|
} else if (code == '0') {
|
|
if (foundPrimaryGrouping) {
|
|
groupingSize++;
|
|
}
|
|
minimumIntegerDigits++;
|
|
maximumIntegerDigits++;
|
|
} else if (code == ',') {
|
|
if (foundPrimaryGrouping == NO) {
|
|
foundPrimaryGrouping = YES;
|
|
} else {
|
|
secondaryGroupingSize = groupingSize;
|
|
groupingSize = 0;
|
|
}
|
|
} else {
|
|
// Anything we don't recognize means we're into the suffix part
|
|
state = STATE_SUFFIX;
|
|
i--;
|
|
}
|
|
break;
|
|
case STATE_FRACTION:
|
|
if (code == '#') {
|
|
maximumFractionDigits++;
|
|
} else if (code == '0') {
|
|
minimumFractionDigits++;
|
|
maximumFractionDigits++;
|
|
} else {
|
|
state = STATE_SUFFIX;
|
|
i--; // and step back one to catch the contents
|
|
}
|
|
break;
|
|
case STATE_SUFFIX:
|
|
suffixBuffer[suffixLength++] = code;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update all valud the parameters
|
|
if (minimumIntegerDigitsp != NULL) {
|
|
*minimumIntegerDigitsp = minimumIntegerDigits;
|
|
}
|
|
|
|
if (maximumIntegerDigitsp != NULL) {
|
|
*maximumIntegerDigitsp = maximumIntegerDigits;
|
|
}
|
|
|
|
if (minimumFractionDigitsp != NULL) {
|
|
*minimumFractionDigitsp = minimumFractionDigits;
|
|
}
|
|
|
|
if (maximumFractionDigitsp != NULL) {
|
|
*maximumFractionDigitsp = maximumFractionDigits;
|
|
}
|
|
|
|
if (groupingSizep != NULL) {
|
|
*groupingSizep = groupingSize;
|
|
}
|
|
|
|
if (secondaryGroupingSizep != NULL) {
|
|
*secondaryGroupingSizep = secondaryGroupingSize;
|
|
}
|
|
|
|
if (prefixLength > 0) {
|
|
*prefix = [[NSString allocWithZone: NULL]
|
|
initWithCharacters: prefixBuffer
|
|
length: prefixLength];
|
|
}
|
|
|
|
if (suffixLength > 0) {
|
|
*suffix = [[NSString allocWithZone: NULL]
|
|
initWithCharacters: suffixBuffer
|
|
length: suffixLength];
|
|
}
|
|
}
|
|
|
|
- (void) extractFromPositiveFormat {
|
|
if ([_positiveFormat length]) {
|
|
|
|
[_positivePrefix release];
|
|
_positivePrefix = nil;
|
|
[_positiveSuffix release];
|
|
_positiveSuffix = nil;
|
|
|
|
extractFormat(_positiveFormat, &_positivePrefix, &_positiveSuffix,
|
|
&_minimumIntegerDigits, &_maximumIntegerDigits,
|
|
&_minimumFractionDigits, &_maximumFractionDigits,
|
|
&_groupingSize, &_secondaryGroupingSize);
|
|
_customMaximumFractionDigits = YES;
|
|
}
|
|
}
|
|
|
|
- (void) extractFromNegativeFormat {
|
|
if ([_negativeFormat length]) {
|
|
|
|
[_negativePrefix release];
|
|
_negativePrefix = nil;
|
|
[_negativeSuffix release];
|
|
_negativeSuffix = nil;
|
|
|
|
extractFormat(_negativeFormat, &_negativePrefix, &_negativeSuffix, NULL,
|
|
NULL, NULL, NULL, NULL, NULL);
|
|
if ([_negativePrefix isEqualToString: @"-"]) {
|
|
// That's not a very interesting prefix...
|
|
[_negativePrefix release];
|
|
_negativePrefix = nil;
|
|
}
|
|
}
|
|
}
|
|
|
|
- initWithCoder: (NSCoder *) coder {
|
|
[super initWithCoder: coder];
|
|
|
|
if ([coder allowsKeyedCoding]) {
|
|
NSDictionary *attributes = [coder decodeObjectForKey: @"NS.attributes"];
|
|
id check;
|
|
|
|
if ((check = [attributes objectForKey: @"formatterBehavior"]) != nil)
|
|
_behavior = [check integerValue];
|
|
if ((check = [attributes objectForKey: @"numberStyle"]) != nil)
|
|
_numberStyle = [check integerValue];
|
|
if ((check = [attributes objectForKey: @"formatWidth"]) != nil)
|
|
_formatWidth = [check integerValue];
|
|
_locale = [[attributes objectForKey: @"locale"] copy];
|
|
_multiplier = [[attributes objectForKey: @"multiplier"] copy];
|
|
if ((check = [attributes objectForKey: @"allowsFloats"]) != nil)
|
|
_allowsFloats = [check boolValue];
|
|
if ((check = [attributes
|
|
objectForKey: @"alwaysShowsDecimalSeparator"]) != nil)
|
|
_alwaysShowsDecimalSeparator = [check boolValue];
|
|
if ((check = [attributes objectForKey: @"lenient"]) != nil)
|
|
_isLenient = [check boolValue];
|
|
_isPartialStringValidationEnabled = NO; // not editable in IB
|
|
if ((check = [attributes objectForKey: @"generatesDecimalNumbers"]) !=
|
|
nil)
|
|
_generatesDecimalNumbers = [check boolValue];
|
|
if ((check = [attributes objectForKey: @"usesGroupingSeparator"]) !=
|
|
nil)
|
|
_usesGroupingSeparator = [check boolValue];
|
|
_usesSignificantDigits = NO; // not editable in IB
|
|
|
|
if ((check = [attributes objectForKey: @"minimumIntegerDigits"]) != nil)
|
|
_minimumIntegerDigits = [check integerValue];
|
|
if ((check = [attributes objectForKey: @"minimumFractionDigits"]) !=
|
|
nil)
|
|
_minimumFractionDigits = [check integerValue];
|
|
_minimumSignificantDigits = 0;
|
|
|
|
if ((check = [attributes objectForKey: @"maximumIntegerDigits"]) != nil)
|
|
_maximumIntegerDigits = [check integerValue];
|
|
if ((check = [attributes objectForKey: @"maximumFractionDigits"]) !=
|
|
nil) {
|
|
_customMaximumFractionDigits = YES;
|
|
_maximumFractionDigits = [check integerValue];
|
|
}
|
|
_maximumSignificantDigits = 0;
|
|
|
|
_minimum = [[attributes objectForKey: @"minimum"] copy];
|
|
_maximum = [[attributes objectForKey: @"maximum"] copy];
|
|
|
|
_nilSymbol = [[attributes objectForKey: @"nilSymbol"] copy];
|
|
_notANumberSymbol =
|
|
[[attributes objectForKey: @"notANumberSymbol"] copy];
|
|
_zeroSymbol = [[attributes objectForKey: @"zeroSymbol"] copy];
|
|
_plusSign = [[attributes objectForKey: @"plusSign"] copy];
|
|
_minusSign = [[attributes objectForKey: @"minusSign"] copy];
|
|
|
|
_negativePrefix = [[attributes objectForKey: @"negativePrefix"] copy];
|
|
_negativeSuffix = [[attributes objectForKey: @"negativeSuffix"] copy];
|
|
_positivePrefix = [[attributes objectForKey: @"positivePrefix"] copy];
|
|
_positiveSuffix = [[attributes objectForKey: @"positiveSuffix"] copy];
|
|
_negativeInfinitySymbol =
|
|
[[attributes objectForKey: @"negativeInfinitySymbol"] copy];
|
|
_positiveInfinitySymbol =
|
|
[[attributes objectForKey: @"positiveInfinitySymbol"] copy];
|
|
|
|
_decimalSeparator =
|
|
[[attributes objectForKey: @"decimalSeparator"] copy];
|
|
_exponentSymbol = [[attributes objectForKey: @"exponentSymbol"] copy];
|
|
_currencyCode = [[attributes objectForKey: @"currencyCode"] copy];
|
|
_currencySymbol = [[attributes objectForKey: @"currencySymbol"] copy];
|
|
_internationalCurrencySymbol = [[attributes
|
|
objectForKey: @"internationalCurrencySymbol"] copy];
|
|
_currencyDecimalSeparator =
|
|
[[attributes objectForKey: @"currencyDecimalSeparator"] copy];
|
|
_currencyGroupingSeparator =
|
|
[[attributes objectForKey: @"currencyGroupingSeparator"] copy];
|
|
_groupingSeparator =
|
|
[[attributes objectForKey: @"groupingSeparator"] copy];
|
|
_groupingSize =
|
|
[[attributes objectForKey: @"groupingSize"] integerValue];
|
|
_secondaryGroupingSize = [[attributes
|
|
objectForKey: @"secondaryGroupingSize"] integerValue];
|
|
_paddingCharacter =
|
|
[[attributes objectForKey: @"paddingCharacter"] copy];
|
|
_paddingPosition =
|
|
[[attributes objectForKey: @"paddingPosition"] integerValue];
|
|
|
|
_percentSymbol = [[attributes objectForKey: @"percentSymbol"] copy];
|
|
_perMillSymbol = [[attributes objectForKey: @"perMillSymbol"] copy];
|
|
_roundingIncrement =
|
|
[[attributes objectForKey: @"roundingIncrement"] copy];
|
|
_roundingMode =
|
|
[[attributes objectForKey: @"roundingMode"] integerValue];
|
|
|
|
_positiveFormat = [[attributes objectForKey: @"positiveFormat"] copy];
|
|
_negativeFormat = [[attributes objectForKey: @"negativeFormat"] copy];
|
|
|
|
[self extractFromPositiveFormat];
|
|
[self extractFromNegativeFormat];
|
|
|
|
_textAttributesForPositiveValues = nil;
|
|
_textAttributesForNegativeValues = nil;
|
|
_textAttributesForNegativeInfinity = nil;
|
|
_textAttributesForNil = nil;
|
|
_textAttributesForNotANumber = nil;
|
|
_textAttributesForPositiveInfinity = nil;
|
|
_textAttributesForZero = nil;
|
|
|
|
// 10.0, these need to be stored separately
|
|
#if 0
|
|
_nilSymbol=[[coder decodeObjectForKey:@"NS.nil"] copy];
|
|
_zeroSymbol=[[coder decodeObjectForKey:@"NS.zero"] copy];
|
|
|
|
_positiveFormat=[[coder decodeObjectForKey:@"NS.positiveformat"] copy];
|
|
_negativeFormat=[[coder decodeObjectForKey:@"NS.negativeformat"] copy];
|
|
_textAttributesForPositiveValues=[[coder decodeObjectForKey:@"NS.positiveattrs"] copy]
|
|
_textAttributesForNegativeValues=[[coder decodeObjectForKey:@"NS.negativeattrs"] copy]
|
|
|
|
_decimalSeparator=[[coder decodeObjectForKey:@"NS.decimal"] copy];
|
|
_thousandSeparator=[[coder decodeObjectForKey:@"NS.thousand"] copy];
|
|
_hasThousandSeparators=[coder decodeBoolForKey:@"NS.hasthousands"];
|
|
_allowsFloats=[coder decodeBoolForKey:@"NS.allowsfloats"];
|
|
_localizesFormat=[coder decodeBoolForKey:@"NS.localized"];
|
|
#endif
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc {
|
|
|
|
[_locale release];
|
|
[_multiplier release];
|
|
|
|
[_minimum release];
|
|
[_maximum release];
|
|
|
|
[_nilSymbol release];
|
|
[_notANumberSymbol release];
|
|
[_zeroSymbol release];
|
|
[_plusSign release];
|
|
[_minusSign release];
|
|
[_negativePrefix release];
|
|
[_negativeSuffix release];
|
|
[_positivePrefix release];
|
|
[_positiveSuffix release];
|
|
[_negativeInfinitySymbol release];
|
|
[_positiveInfinitySymbol release];
|
|
|
|
[_decimalSeparator release];
|
|
[_exponentSymbol release];
|
|
[_currencyCode release];
|
|
[_currencySymbol release];
|
|
[_internationalCurrencySymbol release];
|
|
[_currencyDecimalSeparator release];
|
|
[_currencyGroupingSeparator release];
|
|
[_groupingSeparator release];
|
|
[_paddingCharacter release];
|
|
[_percentSymbol release];
|
|
[_perMillSymbol release];
|
|
[_roundingIncrement release];
|
|
[_positiveFormat release];
|
|
[_negativeFormat release];
|
|
[_textAttributesForPositiveValues release];
|
|
[_textAttributesForNegativeValues release];
|
|
[_textAttributesForNegativeInfinity release];
|
|
[_textAttributesForNil release];
|
|
[_textAttributesForNotANumber release];
|
|
[_textAttributesForPositiveInfinity release];
|
|
[_textAttributesForZero release];
|
|
|
|
[_attributedStringForNil release];
|
|
[_attributedStringForNotANumber release];
|
|
[_attributedStringForZero release];
|
|
[_roundingBehavior release];
|
|
[_thousandSeparator release];
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (NSNumberFormatterBehavior) formatterBehavior {
|
|
return _behavior;
|
|
}
|
|
|
|
- (NSNumberFormatterStyle) numberStyle {
|
|
return _numberStyle;
|
|
}
|
|
|
|
- (NSString *) format {
|
|
return [NSString stringWithFormat: @"%@;%@;%@", _positiveFormat,
|
|
_attributedStringForZero,
|
|
_negativeFormat];
|
|
}
|
|
|
|
- (NSUInteger) formatWidth {
|
|
return _formatWidth;
|
|
}
|
|
|
|
- (NSLocale *) locale {
|
|
return _locale;
|
|
}
|
|
|
|
- (NSNumber *) multiplier {
|
|
if (_multiplier == nil) {
|
|
|
|
if (_numberStyle == NSNumberFormatterPercentStyle)
|
|
return [NSNumber numberWithInt: 100];
|
|
}
|
|
|
|
return _multiplier;
|
|
}
|
|
|
|
- (BOOL) allowsFloats {
|
|
return _allowsFloats;
|
|
}
|
|
|
|
- (BOOL) localizesFormat {
|
|
return _localizesFormat;
|
|
}
|
|
|
|
- (BOOL) hasThousandSeparators {
|
|
return _hasThousandSeparators;
|
|
}
|
|
|
|
- (BOOL) alwaysShowsDecimalSeparator {
|
|
return _alwaysShowsDecimalSeparator;
|
|
}
|
|
|
|
- (BOOL) isLenient {
|
|
return _isLenient;
|
|
}
|
|
|
|
- (BOOL) isPartialStringValidationEnabled {
|
|
return _isPartialStringValidationEnabled;
|
|
}
|
|
|
|
- (BOOL) generatesDecimalNumbers {
|
|
return _generatesDecimalNumbers;
|
|
}
|
|
|
|
- (BOOL) usesGroupingSeparator {
|
|
return _usesGroupingSeparator;
|
|
}
|
|
|
|
- (BOOL) usesSignificantDigits {
|
|
return _usesSignificantDigits;
|
|
}
|
|
|
|
- (NSUInteger) minimumIntegerDigits {
|
|
return _minimumIntegerDigits;
|
|
}
|
|
|
|
- (NSUInteger) minimumFractionDigits {
|
|
return _minimumFractionDigits;
|
|
}
|
|
|
|
- (NSUInteger) minimumSignificantDigits {
|
|
return _minimumSignificantDigits;
|
|
}
|
|
|
|
- (NSUInteger) maximumIntegerDigits {
|
|
return _maximumIntegerDigits;
|
|
}
|
|
|
|
- (NSUInteger) maximumFractionDigits {
|
|
if (_customMaximumFractionDigits)
|
|
return _maximumFractionDigits;
|
|
|
|
if (_numberStyle == NSNumberFormatterDecimalStyle)
|
|
return 3;
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (NSUInteger) maximumSignificantDigits {
|
|
return _maximumSignificantDigits;
|
|
}
|
|
|
|
- (NSNumber *) minimum {
|
|
return _minimum;
|
|
}
|
|
|
|
- (NSNumber *) maximum {
|
|
return _maximum;
|
|
}
|
|
|
|
- (NSString *) nilSymbol {
|
|
return _nilSymbol;
|
|
}
|
|
|
|
- (NSString *) notANumberSymbol {
|
|
if (_notANumberSymbol == nil)
|
|
return @"NaN";
|
|
|
|
return _notANumberSymbol;
|
|
}
|
|
|
|
- (NSString *) zeroSymbol {
|
|
return _zeroSymbol;
|
|
}
|
|
|
|
- (NSString *) plusSign {
|
|
return _plusSign;
|
|
}
|
|
|
|
- (NSString *) minusSign {
|
|
return _minusSign;
|
|
}
|
|
|
|
- (NSString *) negativePrefix {
|
|
if (_negativePrefix == nil)
|
|
return @"";
|
|
|
|
return _negativePrefix;
|
|
}
|
|
|
|
- (NSString *) negativeSuffix {
|
|
// Suffixes return the percent symbol if specified
|
|
|
|
if (_negativeSuffix == nil) {
|
|
|
|
if (_numberStyle == NSNumberFormatterPercentStyle)
|
|
return [self percentSymbol];
|
|
|
|
return @"";
|
|
}
|
|
|
|
return _negativeSuffix;
|
|
}
|
|
|
|
- (NSString *) positivePrefix {
|
|
if (_positivePrefix == nil)
|
|
return @"";
|
|
|
|
return _positivePrefix;
|
|
}
|
|
|
|
- (NSString *) positiveSuffix {
|
|
// Suffixes return the percent symbol if specified
|
|
|
|
if (_positiveSuffix == nil) {
|
|
|
|
if (_numberStyle == NSNumberFormatterPercentStyle)
|
|
return [self percentSymbol];
|
|
|
|
return @"";
|
|
}
|
|
|
|
return _positiveSuffix;
|
|
}
|
|
|
|
- (NSString *) negativeInfinitySymbol {
|
|
return _negativeInfinitySymbol;
|
|
}
|
|
|
|
- (NSString *) positiveInfinitySymbol {
|
|
return _positiveInfinitySymbol;
|
|
}
|
|
|
|
- (NSString *) thousandSeparator {
|
|
return _thousandSeparator;
|
|
}
|
|
|
|
- (NSString *) decimalSeparator {
|
|
return _decimalSeparator;
|
|
}
|
|
|
|
- (NSString *) exponentSymbol {
|
|
return _exponentSymbol;
|
|
}
|
|
|
|
- (NSString *) currencyCode {
|
|
return _currencyCode;
|
|
}
|
|
|
|
- (NSString *) currencySymbol {
|
|
return _currencySymbol;
|
|
}
|
|
|
|
- (NSString *) internationalCurrencySymbol {
|
|
return _internationalCurrencySymbol;
|
|
}
|
|
|
|
- (NSString *) currencyDecimalSeparator {
|
|
return _currencyDecimalSeparator;
|
|
}
|
|
|
|
- (NSString *) currencyGroupingSeparator {
|
|
return _currencyGroupingSeparator;
|
|
}
|
|
|
|
- (NSString *) groupingSeparator {
|
|
if (_groupingSeparator == nil) {
|
|
NSString *check = [_locale objectForKey: NSLocaleGroupingSeparator];
|
|
|
|
if (check != nil)
|
|
return check;
|
|
|
|
return @",";
|
|
}
|
|
|
|
return _groupingSeparator;
|
|
}
|
|
|
|
- (NSUInteger) groupingSize {
|
|
return _groupingSize;
|
|
}
|
|
|
|
- (NSUInteger) secondaryGroupingSize {
|
|
return _secondaryGroupingSize;
|
|
}
|
|
|
|
- (NSString *) paddingCharacter {
|
|
return _paddingCharacter;
|
|
}
|
|
|
|
- (NSNumberFormatterPadPosition) paddingPosition {
|
|
return _paddingPosition;
|
|
}
|
|
|
|
- (NSString *) percentSymbol {
|
|
if (_percentSymbol == nil)
|
|
return @"%";
|
|
|
|
return _percentSymbol;
|
|
}
|
|
|
|
- (NSString *) perMillSymbol {
|
|
return _perMillSymbol;
|
|
}
|
|
|
|
- (NSDecimalNumberHandler *) roundingBehavior {
|
|
return _roundingBehavior;
|
|
}
|
|
|
|
- (NSNumber *) roundingIncrement {
|
|
return _roundingIncrement;
|
|
}
|
|
|
|
- (NSNumberFormatterRoundingMode) roundingMode {
|
|
return _roundingMode;
|
|
}
|
|
|
|
- (NSString *) positiveFormat {
|
|
return _positiveFormat;
|
|
}
|
|
|
|
- (NSString *) negativeFormat {
|
|
return _negativeFormat;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForPositiveValues {
|
|
return _textAttributesForPositiveValues;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForNegativeValues {
|
|
return _textAttributesForNegativeValues;
|
|
}
|
|
|
|
- (NSAttributedString *) attributedStringForNil {
|
|
return _attributedStringForNil;
|
|
}
|
|
|
|
- (NSAttributedString *) attributedStringForNotANumber {
|
|
return _attributedStringForNotANumber;
|
|
}
|
|
|
|
- (NSAttributedString *) attributedStringForZero {
|
|
return _attributedStringForZero;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForNegativeInfinity {
|
|
return _textAttributesForNegativeInfinity;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForNil {
|
|
return _textAttributesForNil;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForNotANumber {
|
|
return _textAttributesForNotANumber;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForPositiveInfinity {
|
|
return _textAttributesForPositiveInfinity;
|
|
}
|
|
|
|
- (NSDictionary *) textAttributesForZero {
|
|
return _textAttributesForZero;
|
|
}
|
|
|
|
- (void) setFormat: (NSString *) format {
|
|
// This is 10.0 behavior only, probably broken anyway
|
|
NSArray *formatStrings = [format componentsSeparatedByString: @";"];
|
|
|
|
_positiveFormat = [[formatStrings objectAtIndex: 0] retain];
|
|
|
|
if ([formatStrings count] == 3) {
|
|
_negativeFormat = [[formatStrings objectAtIndex: 2] retain];
|
|
_attributedStringForZero = [[NSAttributedString allocWithZone: NULL]
|
|
initWithString: [formatStrings objectAtIndex: 1]
|
|
attributes: [NSDictionary dictionary]];
|
|
} else if ([formatStrings count] == 2)
|
|
_negativeFormat = [[formatStrings objectAtIndex: 1] retain];
|
|
else
|
|
_negativeFormat = [NSString stringWithFormat: @"-%@", _positiveFormat];
|
|
|
|
if ([format rangeOfString: @","].location != NSNotFound ||
|
|
[format rangeOfString: _thousandSeparator].location != NSNotFound)
|
|
[self setHasThousandSeparators: YES];
|
|
}
|
|
|
|
- (void) setAllowsFloats: (BOOL) flag {
|
|
_allowsFloats = flag;
|
|
}
|
|
|
|
- (void) setLocalizesFormat: (BOOL) flag {
|
|
_localizesFormat = flag;
|
|
}
|
|
|
|
- (void) setCurrencyCode: (NSString *) value {
|
|
value = [value copy];
|
|
[_currencyCode release];
|
|
_currencyCode = value;
|
|
}
|
|
|
|
- (void) setCurrencyDecimalSeparator: (NSString *) value {
|
|
value = [value copy];
|
|
[_currencyDecimalSeparator release];
|
|
_currencyDecimalSeparator = value;
|
|
}
|
|
|
|
- (void) setCurrencyGroupingSeparator: (NSString *) value {
|
|
value = [value copy];
|
|
[_currencyGroupingSeparator release];
|
|
_currencyGroupingSeparator = value;
|
|
}
|
|
|
|
- (void) setCurrencySymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_currencySymbol release];
|
|
_currencySymbol = value;
|
|
}
|
|
|
|
- (void) setDecimalSeparator: (NSString *) value {
|
|
value = [value copy];
|
|
[_decimalSeparator release];
|
|
_decimalSeparator = value;
|
|
}
|
|
|
|
- (void) setExponentSymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_exponentSymbol release];
|
|
_exponentSymbol = value;
|
|
}
|
|
|
|
- (void) setFormatterBehavior: (NSNumberFormatterBehavior) value {
|
|
_behavior = value;
|
|
}
|
|
|
|
- (void) setFormatWidth: (NSUInteger) value {
|
|
_formatWidth = value;
|
|
}
|
|
|
|
- (void) setGeneratesDecimalNumbers: (BOOL) value {
|
|
_generatesDecimalNumbers = value;
|
|
}
|
|
|
|
- (void) setGroupingSeparator: (NSString *) value {
|
|
value = [value copy];
|
|
[_groupingSeparator release];
|
|
_groupingSeparator = value;
|
|
}
|
|
|
|
- (void) setGroupingSize: (NSUInteger) value {
|
|
_groupingSize = value;
|
|
}
|
|
|
|
- (void) setInternationalCurrencySymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_internationalCurrencySymbol release];
|
|
_internationalCurrencySymbol = value;
|
|
}
|
|
|
|
- (void) setLenient: (BOOL) value {
|
|
_isLenient = value;
|
|
}
|
|
|
|
- (void) setLocale: (NSLocale *) value {
|
|
value = [value copy];
|
|
[_locale release];
|
|
_locale = value;
|
|
}
|
|
|
|
- (void) setMaximumFractionDigits: (NSUInteger) value {
|
|
_customMaximumFractionDigits = YES;
|
|
_maximumFractionDigits = value;
|
|
}
|
|
|
|
- (void) setMaximumIntegerDigits: (NSUInteger) value {
|
|
_maximumIntegerDigits = value;
|
|
}
|
|
|
|
- (void) setMaximumSignificantDigits: (NSUInteger) value {
|
|
_maximumSignificantDigits = value;
|
|
}
|
|
|
|
- (void) setMinimum: (NSNumber *) value {
|
|
value = [value copy];
|
|
[_minimum release];
|
|
_minimum = value;
|
|
}
|
|
|
|
- (void) setMaximum: (NSNumber *) value {
|
|
value = [value copy];
|
|
[_maximum release];
|
|
_maximum = value;
|
|
}
|
|
|
|
- (void) setMinimumFractionDigits: (NSUInteger) value {
|
|
_minimumFractionDigits = value;
|
|
}
|
|
|
|
- (void) setMinimumIntegerDigits: (NSUInteger) value {
|
|
_minimumIntegerDigits = value;
|
|
}
|
|
|
|
- (void) setMinimumSignificantDigits: (NSUInteger) value {
|
|
_minimumSignificantDigits = value;
|
|
}
|
|
|
|
- (void) setMinusSign: (NSString *) value {
|
|
value = [value copy];
|
|
[_minusSign release];
|
|
_minusSign = value;
|
|
}
|
|
|
|
- (void) setMultiplier: (NSNumber *) value {
|
|
value = [value copy];
|
|
[_multiplier release];
|
|
_multiplier = value;
|
|
}
|
|
|
|
- (void) setNegativeInfinitySymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_negativeInfinitySymbol release];
|
|
_negativeInfinitySymbol = value;
|
|
}
|
|
|
|
- (void) setNegativePrefix: (NSString *) value {
|
|
value = [value copy];
|
|
[_negativePrefix release];
|
|
_negativePrefix = value;
|
|
}
|
|
|
|
- (void) setNegativeSuffix: (NSString *) value {
|
|
value = [value copy];
|
|
[_negativeSuffix release];
|
|
_negativeSuffix = value;
|
|
}
|
|
|
|
- (void) setNilSymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_nilSymbol release];
|
|
_nilSymbol = value;
|
|
}
|
|
|
|
- (void) setNotANumberSymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_notANumberSymbol release];
|
|
_notANumberSymbol = value;
|
|
}
|
|
|
|
- (void) setNumberStyle: (NSNumberFormatterStyle) value {
|
|
_numberStyle = value;
|
|
}
|
|
|
|
- (void) setPaddingCharacter: (NSString *) value {
|
|
value = [value copy];
|
|
[_paddingCharacter release];
|
|
_paddingCharacter = value;
|
|
}
|
|
|
|
- (void) setPaddingPosition: (NSNumberFormatterPadPosition) value {
|
|
_paddingPosition = value;
|
|
}
|
|
|
|
- (void) setPartialStringValidationEnabled: (BOOL) value {
|
|
_isPartialStringValidationEnabled = value;
|
|
}
|
|
|
|
- (void) setPercentSymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_percentSymbol release];
|
|
_percentSymbol = value;
|
|
}
|
|
|
|
- (void) setPerMillSymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_perMillSymbol release];
|
|
_perMillSymbol = value;
|
|
}
|
|
|
|
- (void) setPlusSign: (NSString *) value {
|
|
value = [value copy];
|
|
[_plusSign release];
|
|
_plusSign = value;
|
|
}
|
|
|
|
- (void) setPositiveInfinitySymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_positiveInfinitySymbol release];
|
|
_positiveInfinitySymbol = value;
|
|
}
|
|
|
|
- (void) setPositivePrefix: (NSString *) value {
|
|
value = [value copy];
|
|
[_positivePrefix release];
|
|
_positivePrefix = value;
|
|
}
|
|
|
|
- (void) setPositiveSuffix: (NSString *) value {
|
|
value = [value copy];
|
|
[_positiveSuffix release];
|
|
_positiveSuffix = value;
|
|
}
|
|
|
|
- (void) setRoundingBehavior: (NSDecimalNumberHandler *) value {
|
|
value = [value retain];
|
|
[_roundingBehavior release];
|
|
_roundingBehavior = value;
|
|
}
|
|
|
|
- (void) setRoundingIncrement: (NSNumber *) value {
|
|
value = [value copy];
|
|
[_roundingIncrement release];
|
|
_roundingIncrement = value;
|
|
}
|
|
|
|
- (void) setRoundingMode: (NSNumberFormatterRoundingMode) value {
|
|
_roundingMode = value;
|
|
}
|
|
|
|
- (void) setSecondaryGroupingSize: (NSUInteger) value {
|
|
_secondaryGroupingSize = value;
|
|
}
|
|
|
|
- (void) setTextAttributesForNegativeInfinity: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForNegativeInfinity release];
|
|
_textAttributesForNegativeInfinity = value;
|
|
}
|
|
|
|
- (void) setTextAttributesForNil: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForNil release];
|
|
_textAttributesForNil = value;
|
|
}
|
|
|
|
- (void) setTextAttributesForNotANumber: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForNotANumber release];
|
|
_textAttributesForNotANumber = value;
|
|
}
|
|
|
|
- (void) setTextAttributesForPositiveInfinity: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForPositiveInfinity release];
|
|
_textAttributesForPositiveInfinity = value;
|
|
}
|
|
|
|
- (void) setTextAttributesForPositiveValues: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForPositiveValues release];
|
|
_textAttributesForPositiveValues = value;
|
|
}
|
|
|
|
- (void) setTextAttributesForZero: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForZero release];
|
|
_textAttributesForZero = value;
|
|
}
|
|
|
|
- (void) setThousandSeparator: (NSString *) value {
|
|
value = [value copy];
|
|
[_thousandSeparator release];
|
|
_thousandSeparator = value;
|
|
[self setHasThousandSeparators: YES];
|
|
}
|
|
|
|
- (void) setUsesGroupingSeparator: (BOOL) value {
|
|
_usesGroupingSeparator = value;
|
|
}
|
|
|
|
- (void) setUsesSignificantDigits: (BOOL) value {
|
|
_usesSignificantDigits = value;
|
|
}
|
|
|
|
- (void) setZeroSymbol: (NSString *) value {
|
|
value = [value copy];
|
|
[_zeroSymbol release];
|
|
_zeroSymbol = value;
|
|
}
|
|
|
|
- (void) setHasThousandSeparators: (BOOL) value {
|
|
_hasThousandSeparators = value;
|
|
}
|
|
|
|
- (void) setAlwaysShowsDecimalSeparator: (BOOL) value {
|
|
_alwaysShowsDecimalSeparator = value;
|
|
}
|
|
|
|
- (void) setPositiveFormat: (NSString *) value {
|
|
value = [value copy];
|
|
[_positiveFormat release];
|
|
_positiveFormat = value;
|
|
// FIXME: generate formatting values from string
|
|
}
|
|
|
|
- (void) setNegativeFormat: (NSString *) value {
|
|
value = [value copy];
|
|
[_negativeFormat release];
|
|
_negativeFormat = value;
|
|
// FIXME: generate formatting values from string
|
|
}
|
|
|
|
- (void) setTextAttributesForNegativeValues: (NSDictionary *) value {
|
|
value = [value copy];
|
|
[_textAttributesForNegativeValues release];
|
|
_textAttributesForNegativeValues = value;
|
|
}
|
|
|
|
- (void) setAttributedStringForNil: (NSAttributedString *) value {
|
|
value = [value copy];
|
|
[_attributedStringForNil release];
|
|
_attributedStringForNil = value;
|
|
}
|
|
|
|
- (void) setAttributedStringForNotANumber: (NSAttributedString *) value {
|
|
value = [value copy];
|
|
[_attributedStringForNotANumber release];
|
|
_attributedStringForNotANumber = value;
|
|
}
|
|
|
|
- (void) setAttributedStringForZero: (NSAttributedString *) value {
|
|
value = [value copy];
|
|
[_attributedStringForZero release];
|
|
_attributedStringForZero = value;
|
|
}
|
|
|
|
- (NSString *) stringFromNumber10_0: (NSNumber *) number {
|
|
NSUnimplementedMethod();
|
|
return [number description];
|
|
}
|
|
|
|
static NSString *stringWithFormatGrouping(NSString *format, id locale,
|
|
NSString *groupingSeparator,
|
|
NSInteger groupingSize, ...)
|
|
{
|
|
NSUInteger length = 0;
|
|
va_list arguments;
|
|
|
|
va_start(arguments, groupingSize);
|
|
|
|
unichar *unicode = NSCharactersNewWithFormatAndGrouping(
|
|
format, locale, arguments, &length, NULL, groupingSeparator,
|
|
groupingSize);
|
|
|
|
return [NSString_unicodePtrNewNoCopy(NULL, unicode, length, YES)
|
|
autorelease];
|
|
}
|
|
|
|
- (NSString *) _stringFromNumber: (NSNumber *) number {
|
|
NSString *string = nil;
|
|
|
|
if (number == nil)
|
|
string = [self nilSymbol];
|
|
else if (number == (NSNumber *) kCFNumberNaN)
|
|
string = [self notANumberSymbol];
|
|
else if (number == (NSNumber *) kCFNumberPositiveInfinity) {
|
|
NSString *check = [self positiveInfinitySymbol];
|
|
|
|
if (check == nil) {
|
|
unichar code = 0x221E; // unicode infinity
|
|
|
|
check = [NSString stringWithCharacters: &code length: 1];
|
|
}
|
|
|
|
string = check;
|
|
} else if (number == (NSNumber *) kCFNumberNegativeInfinity) {
|
|
NSString *check = [self negativeInfinitySymbol];
|
|
|
|
if (check == nil) {
|
|
unichar codes[2] = {'-', 0x221E}; // unicode infinity
|
|
|
|
check = [NSString stringWithCharacters: codes length: 2];
|
|
}
|
|
|
|
string = check;
|
|
}
|
|
|
|
const char *objcType = [number objCType];
|
|
|
|
if (objcType == NULL || strlen(objcType) != 1)
|
|
objcType = "?";
|
|
|
|
switch (*objcType) {
|
|
case _C_CHR:
|
|
case _C_SHT:
|
|
case _C_INT:
|
|
#ifndef __LP64__
|
|
case _C_LNG:
|
|
#endif
|
|
string = stringWithFormatGrouping(
|
|
@"%i", _locale, [self groupingSeparator], [self groupingSize],
|
|
[number intValue]);
|
|
break;
|
|
|
|
case _C_UCHR:
|
|
case _C_USHT:
|
|
case _C_UINT:
|
|
#ifndef __LP64__
|
|
case _C_ULNG:
|
|
#endif
|
|
string = stringWithFormatGrouping(
|
|
@"%u", _locale, [self groupingSeparator], [self groupingSize],
|
|
[number unsignedIntValue]);
|
|
break;
|
|
|
|
#ifdef __LP64__
|
|
case _C_LNG:
|
|
#endif
|
|
case _C_LNG_LNG:
|
|
string = stringWithFormatGrouping(
|
|
@"%qd", _locale, [self groupingSeparator], [self groupingSize],
|
|
[number longLongValue]);
|
|
break;
|
|
break;
|
|
|
|
#ifdef __LP64__
|
|
case _C_ULNG:
|
|
#endif
|
|
case _C_ULNG_LNG:
|
|
string = stringWithFormatGrouping(
|
|
@"%qu", _locale, [self groupingSeparator], [self groupingSize],
|
|
[number unsignedLongLongValue]);
|
|
break;
|
|
|
|
case _C_FLT:
|
|
case _C_DBL:;
|
|
NSString *format;
|
|
|
|
format = [NSString
|
|
stringWithFormat: @"%%.%df", [self minimumFractionDigits]];
|
|
|
|
string = stringWithFormatGrouping(
|
|
format, _locale, [self groupingSeparator], [self groupingSize],
|
|
[number doubleValue]);
|
|
break;
|
|
|
|
default:
|
|
string = [number description];
|
|
break;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
static NSNumber *multipliedNumber(NSNumber *number, NSNumber *multiplier) {
|
|
if (multiplier == nil)
|
|
return number;
|
|
|
|
return [NSNumber
|
|
numberWithDouble: [number doubleValue] * [multiplier doubleValue]];
|
|
}
|
|
|
|
static BOOL numberIsNegative(NSNumber *number) {
|
|
if (number == nil)
|
|
return NO;
|
|
|
|
return (copysign(1.0, [number doubleValue]) < 0) ? YES : NO;
|
|
}
|
|
|
|
static BOOL numberIsPositive(NSNumber *number) {
|
|
if (number == nil)
|
|
return YES; // ?
|
|
|
|
return (copysign(1.0, [number doubleValue]) > 0) ? YES : NO;
|
|
}
|
|
|
|
- (NSString *) stringFromNumberNoStyle: (NSNumber *) number {
|
|
number = multipliedNumber(number, [self multiplier]);
|
|
|
|
NSString *prefix;
|
|
NSString *suffix;
|
|
// unused
|
|
// NSString *format;
|
|
|
|
if (numberIsNegative(number)) {
|
|
prefix = [self negativePrefix];
|
|
suffix = [self negativeSuffix];
|
|
// format=[self negativeFormat];
|
|
} else {
|
|
prefix = [self positivePrefix];
|
|
suffix = [self positiveSuffix];
|
|
// format=[self positiveFormat];
|
|
}
|
|
|
|
NSString *result;
|
|
|
|
result = prefix;
|
|
result = [result stringByAppendingString: [self _stringFromNumber: number]];
|
|
result = [result stringByAppendingString: suffix];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSString *) stringFromNumberPercentStyle: (NSNumber *) number {
|
|
NSUnimplementedMethod();
|
|
return [[self stringFromNumberNoStyle: number]
|
|
stringByAppendingString: [self percentSymbol]];
|
|
}
|
|
|
|
- (NSString *) stringFromNumber10_4: (NSNumber *) number {
|
|
switch (_numberStyle) {
|
|
|
|
case NSNumberFormatterNoStyle:
|
|
return [self stringFromNumberNoStyle: number];
|
|
|
|
case NSNumberFormatterDecimalStyle:
|
|
return [self stringFromNumberNoStyle: number];
|
|
|
|
case NSNumberFormatterCurrencyStyle:
|
|
NSUnimplementedMethod();
|
|
break;
|
|
|
|
case NSNumberFormatterPercentStyle:
|
|
return [self stringFromNumberNoStyle: number];
|
|
|
|
case NSNumberFormatterScientificStyle:
|
|
NSUnimplementedMethod();
|
|
break;
|
|
|
|
case NSNumberFormatterSpellOutStyle:
|
|
NSUnimplementedMethod();
|
|
break;
|
|
}
|
|
return [number description];
|
|
}
|
|
|
|
- (NSString *) stringFromNumber: (NSNumber *) number {
|
|
NSNumberFormatterBehavior check = _behavior;
|
|
|
|
if (check == NSNumberFormatterBehaviorDefault)
|
|
check = NSNumberFormatterBehavior10_4;
|
|
|
|
if (check == NSNumberFormatterBehavior10_0)
|
|
return [self stringFromNumber10_0: number];
|
|
else
|
|
return [self stringFromNumber10_4: number];
|
|
}
|
|
|
|
- (NSNumber *) _numberFromString: (NSString *) string
|
|
error: (NSString **) error
|
|
{
|
|
// Note: this method is still quite incomplete compared to the thousand of
|
|
// formatting combinations you can set on a number formatter...
|
|
|
|
NSMutableString *mutableString = [[string mutableCopy] autorelease];
|
|
unichar thousandSeparator = [_thousandSeparator characterAtIndex: 0];
|
|
|
|
for (NSUInteger i = 0; i < [mutableString length]; ++i) {
|
|
// take out the thousand separator
|
|
if (_hasThousandSeparators &&
|
|
[mutableString characterAtIndex: i] == thousandSeparator) {
|
|
[mutableString deleteCharactersInRange: NSMakeRange(i, 1)];
|
|
}
|
|
}
|
|
// Locale to use to parse the string
|
|
NSLocale *locale = _locale;
|
|
if (locale == nil) {
|
|
locale = [NSLocale currentLocale];
|
|
}
|
|
if (_decimalSeparator) {
|
|
// Replace the decimal separator to the scanner locale one so the
|
|
// scanner can properly parse the string in case the formatter custom
|
|
// separator doesn't match the locale one
|
|
NSString *localeDecimalSeparator =
|
|
[locale objectForKey: NSLocaleDecimalSeparator];
|
|
if (localeDecimalSeparator) {
|
|
[mutableString
|
|
replaceOccurrencesOfString: _decimalSeparator
|
|
withString: localeDecimalSeparator
|
|
options: 0
|
|
range: NSMakeRange(0,
|
|
[mutableString
|
|
length])];
|
|
}
|
|
}
|
|
|
|
NSScanner *scanner = [NSScanner scannerWithString: mutableString];
|
|
[scanner setLocale: (id) locale];
|
|
|
|
float value;
|
|
NSNumber *number = nil;
|
|
BOOL isValid = YES;
|
|
if ([scanner scanFloat: &value] == NO) {
|
|
isValid = NO;
|
|
} else {
|
|
if (![scanner isAtEnd]) {
|
|
// The number is valid only if the remaining string is the suffix
|
|
NSString *suffix =
|
|
value >= 0 ? [self positiveSuffix] : [self negativeSuffix];
|
|
if ([suffix length]) {
|
|
NSString *remainingString = [[scanner string]
|
|
substringFromIndex: [scanner scanLocation]];
|
|
if (![remainingString isEqualToString: suffix]) {
|
|
isValid = NO;
|
|
}
|
|
} else {
|
|
isValid = NO;
|
|
}
|
|
}
|
|
if (isValid) {
|
|
if ([self multiplier]) {
|
|
value /= [[self multiplier] floatValue];
|
|
}
|
|
number = [NSNumber numberWithFloat: value];
|
|
}
|
|
}
|
|
if (isValid == NO) {
|
|
if (error != NULL) {
|
|
*error = NSLocalizedStringFromTableInBundle(
|
|
@"Invalid number", nil,
|
|
[NSBundle bundleForClass: [NSNumberFormatter class]], @"");
|
|
}
|
|
number = nil;
|
|
}
|
|
return number;
|
|
}
|
|
|
|
- (NSNumber *) numberFromString: (NSString *) string {
|
|
return [self _numberFromString: string error: NULL];
|
|
}
|
|
|
|
// BROKEN
|
|
#if 0
|
|
-(NSString *)_objectValue:(id)object withNumberFormat:(NSString *)format {
|
|
NSString *stringValue = [[NSNumber numberWithDouble:[object doubleValue]] stringValue];
|
|
//NSAllocateMemoryPages wtf??
|
|
unichar *valueBuffer = NSAllocateMemoryPages([stringValue length]+1);
|
|
unichar *formatBuffer = NSAllocateMemoryPages([format length]+1);
|
|
unichar *outputBuffer = NSAllocateMemoryPages([format length]+64);
|
|
BOOL isNegative = [stringValue hasPrefix:@"-"];
|
|
BOOL done = NO;
|
|
NSUInteger formatIndex, valueIndex = 0, outputIndex = 0;
|
|
NSUInteger prePoint, postPoint;
|
|
NSInteger thousandSepCounter;
|
|
|
|
// remove -
|
|
if (isNegative)
|
|
stringValue = [stringValue substringWithRange:NSMakeRange(1, [stringValue length]-1)];
|
|
|
|
prePoint = [stringValue rangeOfString:@"."].location;
|
|
postPoint = [stringValue length] - prePoint - 1;
|
|
|
|
// decremented in main loop, when zero, time for a separator
|
|
if (_hasThousandSeparators)
|
|
thousandSepCounter = (prePoint % 3) ? (prePoint % 3) : 3;
|
|
else
|
|
thousandSepCounter = -1; // never
|
|
|
|
NSLog(@"%@: pre %d post %d sep %d", stringValue, prePoint, postPoint, thousandSepCounter);
|
|
|
|
[format getCharacters:formatBuffer];
|
|
[stringValue getCharacters:valueBuffer];
|
|
|
|
while (!done) {
|
|
switch(formatBuffer[formatIndex]) {
|
|
case NSNumberFormatterThousandSeparator:
|
|
[self setHasThousandSeparators:YES];
|
|
[self setThousandSeparator:[NSString stringWithCharacters:formatBuffer+formatIndex length:1]];
|
|
break;
|
|
|
|
case NSNumberFormatterDecimalSeparator:
|
|
[self setDecimalSeparator:[NSString stringWithCharacters:formatBuffer+formatIndex length:1]];
|
|
break;
|
|
|
|
case NSNumberFormatterPlaceholder:
|
|
case NSNumberFormatterSpace:
|
|
outputBuffer[outputIndex++] = valueBuffer[valueIndex++];
|
|
|
|
if (valueIndex < prePoint) {
|
|
thousandSepCounter--;
|
|
if (thousandSepCounter == 0) {
|
|
outputBuffer[outputIndex++] = [_thousandSeparator characterAtIndex:0];
|
|
thousandSepCounter = 3;
|
|
}
|
|
}
|
|
else if (valueIndex == prePoint)
|
|
outputBuffer[outputIndex++] = [_decimalSeparator characterAtIndex:0];
|
|
else {
|
|
postPoint--;
|
|
if (postPoint == 0)
|
|
done = YES;
|
|
}
|
|
|
|
break;
|
|
|
|
case NSNumberFormatterCurrency:
|
|
// localize
|
|
|
|
default:
|
|
outputBuffer[outputIndex++] = formatBuffer[formatIndex];
|
|
break;
|
|
}
|
|
|
|
formatIndex++;
|
|
}
|
|
|
|
NSLog(@"stringValue %@ format %@", stringValue, format);
|
|
return [NSString stringWithCharacters:outputBuffer length:outputIndex];
|
|
}
|
|
#endif
|
|
|
|
// this section works, but it's pretty lame...
|
|
// it doesn't round, it truncates; integers in the format specifier are
|
|
// ignored...
|
|
- (NSString *) _separatedStringIfNeededWithString: (NSString *) string {
|
|
NSUInteger thousandSepCounter, i, j = 0;
|
|
unichar buffer[256];
|
|
|
|
if (!_hasThousandSeparators)
|
|
return string;
|
|
|
|
if ([string length] < 4)
|
|
return string;
|
|
|
|
thousandSepCounter = ([string length] % 3) ? ([string length] % 3) : 3;
|
|
for (i = 0; i < [string length]; ++i) {
|
|
buffer[j++] = [string characterAtIndex: i];
|
|
thousandSepCounter--;
|
|
if (thousandSepCounter == 0) {
|
|
buffer[j++] = [_thousandSeparator characterAtIndex: 0];
|
|
thousandSepCounter = 3;
|
|
}
|
|
}
|
|
buffer[--j] = (unichar) 0;
|
|
|
|
return [NSString stringWithCharacters: buffer length: j];
|
|
}
|
|
|
|
- (NSString *) _stringValue: (NSString *) stringValue
|
|
withNumberFormat: (NSString *) format
|
|
{
|
|
NSString *rightSide = nil, *leftSide = nil;
|
|
NSMutableString *result = [NSMutableString string];
|
|
NSRange r;
|
|
NSUInteger i, indexRight = 0;
|
|
BOOL formatNoDecPoint =
|
|
([format rangeOfString: @"."].location == NSNotFound);
|
|
BOOL havePassedDecPoint = NO;
|
|
NSInteger lastPlaceholder = 0;
|
|
|
|
// remove negative sign if present
|
|
if ([stringValue hasPrefix: @"-"])
|
|
stringValue = [stringValue
|
|
substringWithRange: NSMakeRange(1, [stringValue length] - 1)];
|
|
|
|
// since we key from the decimal point... if there isn't one in the format
|
|
// spec we have to go on the "last placeholder"; if we have neither decimal
|
|
// NOR placeholders, well, we can't really format the number can we
|
|
if (formatNoDecPoint) {
|
|
lastPlaceholder = [format rangeOfString: @"#"
|
|
options: NSBackwardsSearch]
|
|
.location;
|
|
if (lastPlaceholder == NSNotFound)
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"NSNumberFormatter: Invalid format string"];
|
|
}
|
|
|
|
// split this into left/right strings
|
|
r = [stringValue rangeOfString: @"."];
|
|
if (r.location != NSNotFound) {
|
|
leftSide = [stringValue substringWithRange: NSMakeRange(0, r.location)];
|
|
rightSide = [stringValue
|
|
substringWithRange: NSMakeRange(r.location + 1,
|
|
[stringValue length] -
|
|
r.location - 1)];
|
|
} else
|
|
leftSide = stringValue;
|
|
|
|
// do commas
|
|
leftSide = [self _separatedStringIfNeededWithString: leftSide];
|
|
|
|
// ugh. loop through the format string, looking for the decimal point
|
|
// or the last placeholder. characters other than special are passed through
|
|
for (i = 0; i < [format length]; ++i) {
|
|
unichar ch = [format characterAtIndex: i];
|
|
|
|
switch (ch) {
|
|
case NSNumberFormatterPlaceholder:
|
|
if (formatNoDecPoint && i == lastPlaceholder)
|
|
[result appendString: leftSide];
|
|
break;
|
|
|
|
// ignore?
|
|
case NSNumberFormatterSpace:
|
|
// ignore; already handled
|
|
case NSNumberFormatterThousandSeparator:
|
|
break;
|
|
|
|
case NSNumberFormatterDecimalSeparator:
|
|
[result appendString: leftSide];
|
|
[result appendString: _decimalSeparator];
|
|
havePassedDecPoint = YES;
|
|
break;
|
|
|
|
case NSNumberFormatterCurrency:
|
|
// FIX, add localization
|
|
|
|
default:
|
|
if (ch >= (unichar) '0' && ch <= (unichar) '9') {
|
|
if (havePassedDecPoint == YES) {
|
|
ch = [rightSide characterAtIndex: indexRight++];
|
|
if (ch == (unichar) 0)
|
|
ch = (unichar) '0';
|
|
} else
|
|
break;
|
|
}
|
|
|
|
[result appendString: [NSString stringWithCharacters: &ch
|
|
length: 1]];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSString *) stringForObjectValue: object {
|
|
if ([object isKindOfClass: [NSNumber class]])
|
|
return [self stringFromNumber: object];
|
|
else
|
|
return [object description];
|
|
}
|
|
|
|
- (NSAttributedString *) attributedStringForObjectValue10_0: object
|
|
withDefaultAttributes:
|
|
(NSDictionary *) defaultAttributes
|
|
{
|
|
if (object == nil) {
|
|
NSAttributedString *check = [self attributedStringForNil];
|
|
|
|
if (check != nil)
|
|
return check;
|
|
}
|
|
if (object == (NSNumber *) kCFNumberNaN) {
|
|
NSAttributedString *check = [self attributedStringForNotANumber];
|
|
|
|
if (check != nil)
|
|
return check;
|
|
}
|
|
|
|
NSString *string = [self stringForObjectValue: object];
|
|
NSDictionary *attributes = nil;
|
|
|
|
if (numberIsPositive(object))
|
|
attributes = [self textAttributesForPositiveValues];
|
|
else if (numberIsNegative(object))
|
|
attributes = [self textAttributesForNegativeValues];
|
|
else
|
|
attributes = [self textAttributesForZero];
|
|
|
|
if (attributes == nil)
|
|
attributes = defaultAttributes;
|
|
|
|
return [[[NSAttributedString allocWithZone: NULL]
|
|
initWithString: string
|
|
attributes: attributes] autorelease];
|
|
}
|
|
|
|
- (NSAttributedString *) attributedStringForObjectValue10_4: object
|
|
withDefaultAttributes:
|
|
(NSDictionary *) defaultAttributes
|
|
{
|
|
NSString *string = [self stringForObjectValue: object];
|
|
NSDictionary *attributes = nil;
|
|
|
|
if (object == nil)
|
|
attributes = [self textAttributesForNil];
|
|
else if (object == (NSNumber *) kCFNumberNaN)
|
|
attributes = [self textAttributesForNotANumber];
|
|
else if (object == (NSNumber *) kCFNumberPositiveInfinity)
|
|
attributes = [self textAttributesForPositiveInfinity];
|
|
else if (object == (NSNumber *) kCFNumberNegativeInfinity)
|
|
attributes = [self textAttributesForNegativeInfinity];
|
|
else if (numberIsPositive(object))
|
|
attributes = [self textAttributesForPositiveValues];
|
|
else if (numberIsNegative(object))
|
|
attributes = [self textAttributesForNegativeValues];
|
|
else
|
|
attributes = [self textAttributesForZero];
|
|
|
|
if (attributes == nil)
|
|
attributes = defaultAttributes;
|
|
|
|
return [[[NSAttributedString allocWithZone: NULL]
|
|
initWithString: string
|
|
attributes: attributes] autorelease];
|
|
}
|
|
|
|
- (NSAttributedString *) attributedStringForObjectValue: object
|
|
withDefaultAttributes:
|
|
(NSDictionary *) attributes
|
|
{
|
|
NSNumberFormatterBehavior check = _behavior;
|
|
|
|
if (check == NSNumberFormatterBehaviorDefault)
|
|
check = NSNumberFormatterBehavior10_4;
|
|
|
|
if (check == NSNumberFormatterBehavior10_0)
|
|
return [self attributedStringForObjectValue10_0: object
|
|
withDefaultAttributes: (NSDictionary *)
|
|
attributes];
|
|
else
|
|
return [self attributedStringForObjectValue10_4: object
|
|
withDefaultAttributes: (NSDictionary *)
|
|
attributes];
|
|
}
|
|
|
|
- (NSString *) editingStringForObjectValue: (id) object {
|
|
return [self stringForObjectValue: object];
|
|
}
|
|
|
|
- (BOOL) getObjectValue: (id *) valuep
|
|
forString: (NSString *) string
|
|
range: (NSRange *) rangep
|
|
error: (NSError **) errorp
|
|
{
|
|
NSString *errorDescription = nil;
|
|
BOOL result = [self getObjectValue: valuep
|
|
forString: string
|
|
errorDescription: &errorDescription];
|
|
if (errorp) {
|
|
if (result) {
|
|
*errorp = nil;
|
|
} else {
|
|
NSDictionary *userInfo = [NSDictionary
|
|
dictionaryWithObject: errorDescription
|
|
forKey: NSLocalizedDescriptionKey];
|
|
*errorp = [NSError errorWithDomain: NSCocoaErrorDomain
|
|
code: 2048
|
|
userInfo: userInfo];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
- (BOOL) getObjectValue: (id *) object
|
|
forString: (NSString *) string
|
|
errorDescription: (NSString **) error
|
|
{
|
|
if (object) {
|
|
*object = nil;
|
|
}
|
|
NSNumber *number = [self _numberFromString: string error: error];
|
|
if (number) {
|
|
float value = [number floatValue];
|
|
if ([self maximum] && value > [[self maximum] floatValue]) {
|
|
if (error != NULL) {
|
|
*error = NSLocalizedStringFromTableInBundle(
|
|
@"Number too big", nil,
|
|
[NSBundle bundleForClass: [NSNumberFormatter class]],
|
|
@"");
|
|
}
|
|
number = nil;
|
|
} else if ([self minimum] && value < [[self minimum] floatValue]) {
|
|
if (error != NULL) {
|
|
*error = NSLocalizedStringFromTableInBundle(
|
|
@"Number too smaller", nil,
|
|
[NSBundle bundleForClass: [NSNumberFormatter class]],
|
|
@"");
|
|
}
|
|
number = nil;
|
|
} else {
|
|
if (object) {
|
|
*object = number;
|
|
}
|
|
}
|
|
}
|
|
|
|
return number != nil;
|
|
}
|
|
|
|
- (BOOL) isPartialStringValid: (NSString *) partialString
|
|
newEditingString: (NSString **) newString
|
|
errorDescription: (NSString **) error
|
|
{
|
|
//
|
|
return YES;
|
|
}
|
|
|
|
@end
|