Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

257 lines (217 sloc) 9.667 kb
/*
* CPNumberFormatter.j
* Foundation
*
* Created by Alexander Ljungberg.
* Copyright 2011, WireLoad Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "Ref.h"
@import <Foundation/CPString.j>
@import <Foundation/CPFormatter.j>
@import <Foundation/CPDecimalNumber.j>
#define UPDATE_NUMBER_HANDLER_IF_NECESSARY() if (!_numberHandler) \
_numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode scale:_maximumFractionDigits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES];
#define SET_NEEDS_NUMBER_HANDLER_UPDATE() _numberHandler = nil;
CPNumberFormatterNoStyle = 0;
CPNumberFormatterDecimalStyle = 1;
CPNumberFormatterCurrencyStyle = 2;
CPNumberFormatterPercentStyle = 3;
CPNumberFormatterScientificStyle = 4;
CPNumberFormatterSpellOutStyle = 5;
CPNumberFormatterRoundCeiling = CPRoundUp;
CPNumberFormatterRoundFloor = CPRoundDown;
CPNumberFormatterRoundDown = CPRoundDown;
CPNumberFormatterRoundUp = CPRoundUp;
CPNumberFormatterRoundHalfEven = CPRoundBankers;
CPNumberFormatterRoundHalfDown = _CPRoundHalfDown;
CPNumberFormatterRoundHalfUp = CPRoundPlain;
/*!
@ingroup foundation
@class CPNumberFormatter
CPNumberFormatter takes a numeric CPNumber value and formats it as text for
display. It also supports the converse, taking text and interpreting it as a
CPNumber by configurable formatting rules.
*/
@implementation CPNumberFormatter : CPFormatter
{
CPNumberFormatterStyle _numberStyle @accessors(property=numberStyle);
CPString _perMillSymbol @accessors(property=perMillSymbol);
CPString _groupingSeparator @accessors(property=groupingSeparator);
CPNumberFormatterRoundingMode _roundingMode @accessors(property=roundingMode);
CPUInteger _minimumFractionDigits @accessors(property=minimumFractionDigits);
CPUInteger _maximumFractionDigits @accessors(property=maximumFractionDigits);
CPString _currencyCode @accessors(property=currencyCode);
CPString _currencySymbol @accessors(property=currencySymbol);
BOOL _generatesDecimalNumbers @accessors(property=generatesDecimalNumbers);
// Note that we do not implement the 10.0 style number formatter, but the 10.4+ formatter. Therefore
// we don't expose this through a `roundingBehavior` property.
CPDecimalNumberHandler _numberHandler;
}
- (id)init
{
if (self = [super init])
{
_roundingMode = CPNumberFormatterRoundHalfEven;
_minimumFractionDigits = 0;
_maximumFractionDigits = 0;
_groupingSeparator = @",";
_generatesDecimalNumbers = YES;
// FIXME Add locale support.
_currencyCode = @"USD";
_currencySymbol = @"$";
}
return self;
}
- (CPString)stringFromNumber:(CPNumber)number
{
var dcmn = [number isKindOfClass:CPDecimalNumber] ? number : [[CPDecimalNumber alloc] _initWithJSNumber:number];
// TODO Add locale support.
switch (_numberStyle)
{
case CPNumberFormatterCurrencyStyle:
case CPNumberFormatterDecimalStyle:
UPDATE_NUMBER_HANDLER_IF_NECESSARY();
dcmn = [dcmn decimalNumberByRoundingAccordingToBehavior:_numberHandler];
var output = [dcmn descriptionWithLocale:nil],
parts = [output componentsSeparatedByString:"."], // FIXME Locale specific.
preFraction = parts[0],
fraction = parts.length > 1 ? parts[1] : "",
preFractionLength = [preFraction length],
commaPosition = 3;
while (fraction.length < _minimumFractionDigits)
fraction += "0";
// TODO This is just a temporary solution. Should be generalised.
// Add in thousands separators.
if (_groupingSeparator)
{
for (var commaPosition = 3, prefLength = [preFraction length]; commaPosition < prefLength; commaPosition += 4)
{
preFraction = [preFraction stringByReplacingCharactersInRange:CPMakeRange(prefLength - commaPosition, 0) withString:_groupingSeparator];
prefLength += 1;
}
}
var string = preFraction;
if (fraction)
string += "." + fraction;
if (_numberStyle === CPNumberFormatterCurrencyStyle)
{
if (_currencySymbol)
string = _currencySymbol + string;
else
string = _currencyCode + string;
}
return string;
default:
return [number description];
}
}
- (CPNumber)numberFromString:(CPString)aString
{
if (_generatesDecimalNumbers)
return [CPDecimalNumber decimalNumberWithString:aString];
else
return parseFloat(aString);
}
- (CPString)stringForObjectValue:(id)anObject
{
if ([anObject isKindOfClass:[CPNumber class]])
return [self stringFromNumber:anObject];
else
return [anObject description];
}
- (CPString)editingStringForObjectValue:(id)anObject
{
return [self stringForObjectValue:anObject];
}
- (BOOL)getObjectValue:(id)anObject forString:(CPString)aString errorDescription:(CPString)anError
{
// TODO Error handling.
var value = [self numberFromString:aString];
AT_DEREF(anObject, value);
return YES;
}
- (void)setNumberStyle:(CPNumberFormatterStyle)aStyle
{
_numberStyle = aStyle;
switch (aStyle)
{
case CPNumberFormatterDecimalStyle:
_minimumFractionDigits = 0;
_maximumFractionDigits = 3;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
break;
case CPNumberFormatterCurrencyStyle:
_minimumFractionDigits = 2;
_maximumFractionDigits = 2;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
break;
}
}
- (void)setRoundingMode:(CPNumberFormatterRoundingMode)aRoundingMode
{
_roundingMode = aRoundingMode;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
- (void)setMinimumFractionDigits:(CPUInteger)aNumber
{
_minimumFractionDigits = aNumber;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
- (void)setMaximumFractionDigits:(CPUInteger)aNumber
{
_maximumFractionDigits = aNumber;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
@end
var CPNumberFormatterStyleKey = "CPNumberFormatterStyleKey",
CPNumberFormatterMinimumFractionDigitsKey = @"CPNumberFormatterMinimumFractionDigitsKey",
CPNumberFormatterMaximumFractionDigitsKey = @"CPNumberFormatterMaximumFractionDigitsKey",
CPNumberFormatterRoundingModeKey = @"CPNumberFormatterRoundingModeKey",
CPNumberFormatterGroupingSeparatorKey = @"CPNumberFormatterGroupingSeparatorKey",
CPNumberFormatterCurrencyCodeKey = @"CPNumberFormatterCurrencyCodeKey",
CPNumberFormatterCurrencySymbolKey = @"CPNumberFormatterCurrencySymbolKey",
CPNumberFormatterGeneratesDecimalNumbers = @"CPNumberFormatterGeneratesDecimalNumbers";
@implementation CPNumberFormatter (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_numberStyle = [aCoder decodeIntForKey:CPNumberFormatterStyleKey];
_minimumFractionDigits = [aCoder decodeIntForKey:CPNumberFormatterMinimumFractionDigitsKey];
_maximumFractionDigits = [aCoder decodeIntForKey:CPNumberFormatterMaximumFractionDigitsKey];
_roundingMode = [aCoder decodeIntForKey:CPNumberFormatterRoundingModeKey];
_groupingSeparator = [aCoder decodeObjectForKey:CPNumberFormatterGroupingSeparatorKey];
_currencyCode = [aCoder decodeObjectForKey:CPNumberFormatterCurrencyCodeKey];
_currencySymbol = [aCoder decodeObjectForKey:CPNumberFormatterCurrencySymbolKey];
_generatesDecimalNumbers = [aCoder decodeBoolForKey:CPNumberFormatterGeneratesDecimalNumbers];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_numberStyle forKey:CPNumberFormatterStyleKey];
[aCoder encodeInt:_minimumFractionDigits forKey:CPNumberFormatterMinimumFractionDigitsKey];
[aCoder encodeInt:_maximumFractionDigits forKey:CPNumberFormatterMaximumFractionDigitsKey];
[aCoder encodeInt:_roundingMode forKey:CPNumberFormatterRoundingModeKey];
[aCoder encodeObject:_groupingSeparator forKey:CPNumberFormatterGroupingSeparatorKey];
[aCoder encodeObject:_currencyCode forKey:CPNumberFormatterCurrencyCodeKey];
[aCoder encodeObject:_currencySymbol forKey:CPNumberFormatterCurrencySymbolKey];
[aCoder encodeBool:_generatesDecimalNumbers forKey:CPNumberFormatterGeneratesDecimalNumbers];
}
@end
Jump to Line
Something went wrong with that request. Please try again.