New: minimum/maximum support for CPNumberFormatter

Previously, minimum and maximum were not supported in CPNumberFormatter.

This commit adds support for these in IB and via code. Updated unit tests included.

Fixes #1829
This commit is contained in:
Andrew Hankinson
2013-03-08 09:30:31 -05:00
committed by Aparajita Fishman
parent 72c277d9ee
commit 5fac631f6f
3 changed files with 101 additions and 7 deletions
+51 -3
View File
@@ -63,6 +63,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
CPNumberFormatterRoundingMode _roundingMode @accessors(property=roundingMode);
CPUInteger _minimumFractionDigits @accessors(property=minimumFractionDigits);
CPUInteger _maximumFractionDigits @accessors(property=maximumFractionDigits);
CPUInteger _minimum @accessors(property=minimum);
CPUInteger _maximum @accessors(property=maximum);
CPString _currencyCode @accessors(property=currencyCode);
CPString _currencySymbol @accessors(property=currencySymbol);
BOOL _generatesDecimalNumbers @accessors(property=generatesDecimalNumbers);
@@ -81,6 +83,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
_maximumFractionDigits = 0;
_groupingSeparator = @",";
_generatesDecimalNumbers = YES;
_minimum = nil;
_maximum = nil;
// FIXME Add locale support.
_currencyCode = @"USD";
@@ -181,8 +185,29 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
- (BOOL)getObjectValue:(id)anObject forString:(CPString)aString errorDescription:(CPString)anError
{
// TODO Error handling.
var value = [self numberFromString:aString];
// allows an empty string to pass without validation
if (aString === @"")
return YES;
var value = [self numberFromString:aString],
error = @"";
// this will return false if we've received anything but a number, most likely NaN
if (!isFinite(value))
error = @"Value is not a number";
else if (_minimum !== nil && value < _minimum)
error = @"Value is less than the minimum allowed value";
else if (_maximum !== nil && value > _maximum)
error = @"Value is greater than the maximum allowed value";
if (error)
{
if (anError)
AT_DEREF(anError, error);
return NO;
}
AT_DEREF(anObject, value);
return YES;
@@ -226,12 +251,29 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
- (void)setMinimum:(CPUInteger)aNumber
{
_minimum = aNumber;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
- (void)setMaximum:(CPUInteger)aNumber
{
_maximum = aNumber;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
#pragma mark Private
- (void)_updateNumberHandlerIfNecessary
{
if (!_numberHandler)
_numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode scale:_maximumFractionDigits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES];
_numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode
scale:_maximumFractionDigits
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
}
@end
@@ -239,6 +281,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
var CPNumberFormatterStyleKey = @"CPNumberFormatterStyleKey",
CPNumberFormatterMinimumFractionDigitsKey = @"CPNumberFormatterMinimumFractionDigitsKey",
CPNumberFormatterMaximumFractionDigitsKey = @"CPNumberFormatterMaximumFractionDigitsKey",
CPNumberFormatterMinimumKey = @"CPNumberFormatterMinimumKey",
CPNumberFormatterMaximumKey = @"CPNumberFormatterMaximumKey",
CPNumberFormatterRoundingModeKey = @"CPNumberFormatterRoundingModeKey",
CPNumberFormatterGroupingSeparatorKey = @"CPNumberFormatterGroupingSeparatorKey",
CPNumberFormatterCurrencyCodeKey = @"CPNumberFormatterCurrencyCodeKey",
@@ -256,6 +300,8 @@ var CPNumberFormatterStyleKey = @"CPNumberFormatterStyleKey",
_numberStyle = [aCoder decodeIntForKey:CPNumberFormatterStyleKey];
_minimumFractionDigits = [aCoder decodeIntForKey:CPNumberFormatterMinimumFractionDigitsKey];
_maximumFractionDigits = [aCoder decodeIntForKey:CPNumberFormatterMaximumFractionDigitsKey];
_minimum = [aCoder decodeIntForKey:CPNumberFormatterMinimumKey];
_maximum = [aCoder decodeIntForKey:CPNumberFormatterMaximumKey];
_roundingMode = [aCoder decodeIntForKey:CPNumberFormatterRoundingModeKey];
_groupingSeparator = [aCoder decodeObjectForKey:CPNumberFormatterGroupingSeparatorKey];
_currencyCode = [aCoder decodeObjectForKey:CPNumberFormatterCurrencyCodeKey];
@@ -273,6 +319,8 @@ var CPNumberFormatterStyleKey = @"CPNumberFormatterStyleKey",
[aCoder encodeInt:_numberStyle forKey:CPNumberFormatterStyleKey];
[aCoder encodeInt:_minimumFractionDigits forKey:CPNumberFormatterMinimumFractionDigitsKey];
[aCoder encodeInt:_maximumFractionDigits forKey:CPNumberFormatterMaximumFractionDigitsKey];
[aCoder encodeInt:_minimum forKey:CPNumberFormatterMinimumKey];
[aCoder encodeInt:_maximum forKey:CPNumberFormatterMaximumKey];
[aCoder encodeInt:_roundingMode forKey:CPNumberFormatterRoundingModeKey];
[aCoder encodeObject:_groupingSeparator forKey:CPNumberFormatterGroupingSeparatorKey];
[aCoder encodeObject:_currencyCode forKey:CPNumberFormatterCurrencyCodeKey];
+46
View File
@@ -48,6 +48,52 @@
[self assert:@"1 234 567" equals:[numberFormatter stringFromNumber:[CPNumber numberWithInt:1234567]]];
}
- (void)testMinimumMaximumValues
{
var numberFormatter = [CPNumberFormatter new],
noneFunction = function() {};
[numberFormatter setMinimum:10];
[numberFormatter setMaximum:20];
[self assertTrue:[numberFormatter getObjectValue:noneFunction forString:@"10" errorDescription:nil]
message:@"MinMax T1: Expected True."];
[self assertTrue:[numberFormatter getObjectValue:noneFunction forString:@"15" errorDescription:nil]
message:@"MinMax T2: Expected True."];
[self assertTrue:[numberFormatter getObjectValue:noneFunction forString:@"20" errorDescription:nil]
message:@"MinMax T3: Expected True."];
[self assertTrue:[numberFormatter getObjectValue:noneFunction forString:@"020" errorDescription:nil]
message:@"MinMax T4: Expected True."];
// check if this formatter behaves like Cocoa, which allows a blank string to pass
[self assertTrue:[numberFormatter getObjectValue:noneFunction forString:@"" errorDescription:nil]
message:@"MinMax T5: Expected True."];
[self assertFalse:[numberFormatter getObjectValue:noneFunction forString:@"-1" errorDescription:nil]
message:@"MinMax T6: Expected False."];
[self assertFalse:[numberFormatter getObjectValue:noneFunction forString:@"1" errorDescription:nil]
message:@"MinMax T7: Expected False."];
[self assertFalse:[numberFormatter getObjectValue:noneFunction forString:@"100" errorDescription:nil]
message:@"MinMax T8: Expected False."];
var testErrorDescription = @"",
testWithString = [numberFormatter getObjectValue:noneFunction
forString:@"Cappuccino"
errorDescription:function(value)
{
if (arguments.length)
return testErrorDescription = value;
return testErrorDescription;
}];
[self assertFalse:testWithString
message:@"MinMax T9: Expected False."];
[self assert:testErrorDescription
equals:@"Value is not a number"
message:@"MinMax T10: Error string does not match."];
}
- (void)testRoundingMode
{
var numberFormatter = [[CPNumberFormatter alloc] init],
+4 -4
View File
@@ -35,11 +35,11 @@
[self setNumberStyle:[attributes valueForKey:@"numberStyle"] || CPNumberFormatterNoStyle];
// if ([attributes containsKey:@"minimum"])
// [self setMinimum:[attributes valueForKey:@"minimum"]];
if ([attributes containsKey:@"minimum"])
[self setMinimum:[attributes valueForKey:@"minimum"]];
// if ([attributes containsKey:@"maximum"])
// [self setMaximum:[attributes valueForKey:@"maximum"]];
if ([attributes containsKey:@"maximum"])
[self setMaximum:[attributes valueForKey:@"maximum"]];
}
return self;