/* * 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 @import @import #define UPDATE_NUMBER_HANDLER_IF_NECESSARY() if (!_numberHandler) \ _numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode scale:_maximumFractionalDigits 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 NSNumber 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); CPNumberFormatterRoundingMode _roundingMode @accessors(property=roundingMode); CPUInteger _maximumFractionalDigits @accessors(property=maximalFractionalDigits); CPDecimalNumberHandler _numberHandler; } - (id)init { if (self = [super init]) { _roundingMode = CPNumberFormatterRoundHalfUp; _maximumFractionalDigits = 3; } return self; } - (CPString)stringFromNumber:(CPNumber)number { // TODO Add locale support. switch(_numberStyle) { case CPNumberFormatterDecimalStyle: UPDATE_NUMBER_HANDLER_IF_NECESSARY(); var dcmn = [CPDecimalNumber numberWithFloat:number]; 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, perMillSymbol = [self _effectivePerMillSymbol]; // TODO This is just a temporary solution. Should be generalised. // Add in thousands separators. if (perMillSymbol) while(commaPosition < [preFraction length]) { preFraction = [preFraction stringByReplacingCharactersInRange:CPMakeRange(commaPosition, 0) withString:perMillSymbol]; commaPosition += 4; } if (fraction) return preFraction + "." + fraction; else return preFraction; default: return [number description]; } } - (CPNumber)numberFromString:(CPString)string { // TODO return parseFloat(string); } - (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; } /*! @ignore Return the perMillSymbol if set, otherwise the locale default. */ - (CPString)_effectivePerMillSymbol { if (_perMillSymbol === nil || _perMillSymbol === undefined) return ","; // (FIXME US Locale specific.) return _perMillSymbol; } - (void)setRoundingMode:(CPNumberFormatterRoundingMode)aRoundingMode { _roundingMode = aRoundingMode; SET_NEEDS_NUMBER_HANDLER_UPDATE(); } - (void)setMaximumFractionDigits:(CPUInteger)aNumber { _maximumFractionalDigits = aNumber; SET_NEEDS_NUMBER_HANDLER_UPDATE(); } @end var CPNumberFormatterStyleKey = "CPNumberFormatterStyleKey"; @implementation CPNumberFormatter (CPCoding) - (id)initWithCoder:(CPCoder)aCoder { self = [super initWithCoder:aCoder]; if (self) { _numberStyle = [aCoder decodeIntForKey:CPNumberFormatterStyleKey]; } return self; } - (void)encodeWithCoder:(CPCoder)aCoder { [super encodeWithCoder:aCoder]; [aCoder encodeInt:_numberStyle forKey:CPNumberFormatterStyleKey]; } @end