diff --git a/Foundation/CPNumberFormatter.j b/Foundation/CPNumberFormatter.j index b942b1d46..0eb70c106 100644 --- a/Foundation/CPNumberFormatter.j +++ b/Foundation/CPNumberFormatter.j @@ -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]; diff --git a/Tests/Foundation/CPNumberFormatterTest.j b/Tests/Foundation/CPNumberFormatterTest.j index 6063d881e..4663cd01f 100644 --- a/Tests/Foundation/CPNumberFormatterTest.j +++ b/Tests/Foundation/CPNumberFormatterTest.j @@ -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], diff --git a/Tools/nib2cib/NSNumberFormatter.j b/Tools/nib2cib/NSNumberFormatter.j index 3eca40a74..e54858a36 100644 --- a/Tools/nib2cib/NSNumberFormatter.j +++ b/Tools/nib2cib/NSNumberFormatter.j @@ -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;