blob: 95796e9ea48145f1847b1c49d4ee9bf5c6b8033c [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.formatters
{
/**
* The NumberBase class is a utility class that contains
* general number formatting capabilities, including rounding,
* precision, thousands formatting, and negative sign formatting.
* The implementation of the formatter classes use this class.
*
* @see mx.formatters.NumberFormatter
* @see mx.formatters.NumberBaseRoundType
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class NumberBase
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param decimalSeparatorFrom Decimal separator to use
* when parsing an input String.
*
* @param thousandsSeparatorFrom Character to use
* as the thousands separator in the input String.
*
* @param decimalSeparatorTo Decimal separator character to use
* when outputting formatted decimal numbers.
*
* @param thousandsSeparatorTo Character to use
* as the thousands separator in the output String.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function NumberBase(decimalSeparatorFrom:String = ".",
thousandsSeparatorFrom:String = ",",
decimalSeparatorTo:String = ".",
thousandsSeparatorTo:String = ",")
{
super();
this.decimalSeparatorFrom = decimalSeparatorFrom;
this.thousandsSeparatorFrom = thousandsSeparatorFrom;
this.decimalSeparatorTo = decimalSeparatorTo;
this.thousandsSeparatorTo = thousandsSeparatorTo;
isValid = true;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// decimalSeparatorFrom
//----------------------------------
/**
* Decimal separator character to use
* when parsing an input String.
*
* @default "."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var decimalSeparatorFrom:String;
//----------------------------------
// decimalSeparatorTo
//----------------------------------
/**
* Decimal separator character to use
* when outputting formatted decimal numbers.
*
* @default "."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var decimalSeparatorTo:String;
//----------------------------------
// isValid
//----------------------------------
/**
* If <code>true</code>, the format succeeded,
* otherwise it is <code>false</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var isValid:Boolean = false;
//----------------------------------
// thousandsSeparatorFrom
//----------------------------------
/**
* Character to use as the thousands separator
* in the input String.
*
* @default ","
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var thousandsSeparatorFrom:String;
//----------------------------------
// thousandsSeparatorTo
//----------------------------------
/**
* Character to use as the thousands separator
* in the output String.
*
* @default ","
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var thousandsSeparatorTo:String;
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Formats a number by rounding it.
* The possible rounding types are defined by
* mx.formatters.NumberBaseRoundType.
*
* @param value Value to be rounded.
*
* @param roundType The type of rounding to perform:
* NumberBaseRoundType.NONE, NumberBaseRoundType.UP,
* NumberBaseRoundType.DOWN, or NumberBaseRoundType.NEAREST.
*
* @return Formatted number.
*
* @see mx.formatters.NumberBaseRoundType
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatRounding(value:String, roundType:String):String
{
var v:Number = Number(value);
if (roundType != NumberBaseRoundType.NONE)
{
if (roundType == NumberBaseRoundType.UP)
{
v = Math.ceil(v);
}
else if (roundType == NumberBaseRoundType.DOWN)
{
v = Math.floor(v);
}
else if (roundType == NumberBaseRoundType.NEAREST)
{
v = Math.round(v);
}
else
{
isValid = false;
return "";
}
}
return v.toString();
}
/**
* Formats a number by rounding it and setting the decimal precision.
* The possible rounding types are defined by
* mx.formatters.NumberBaseRoundType.
*
* @param value Value to be rounded.
*
* @param roundType The type of rounding to perform:
* NumberBaseRoundType.NONE, NumberBaseRoundType.UP,
* NumberBaseRoundType.DOWN, or NumberBaseRoundType.NEAREST.
*
* @param precision int of decimal places to use.
*
* @return Formatted number.
*
* @see mx.formatters.NumberBaseRoundType
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatRoundingWithPrecision(value:String, roundType:String,
precision:int):String
{
// precision works differently now. Its default value is -1
// which means 'do not alter the number's precision'. If a precision
// value is set, all numbers will contain that precision. Otherwise, there
// precision will not be changed.
var v:Number = Number(value);
// If rounding is not present and precision is NaN,
// leave value untouched.
if (roundType == NumberBaseRoundType.NONE)
{
if (precision == -1)
return v.toString();
}
else
{
// If rounding is present but precision is less than 0,
// then do integer rounding.
if (precision < 0)
precision = 0;
// Shift decimal right as Math functions
// perform only integer ceil/round/floor.
v = v * Math.pow(10, precision);
// Attempt to get rid of floating point errors
v = Number(v.toString());
if (roundType == NumberBaseRoundType.UP)
{
v = Math.ceil(v);
}
else if (roundType == NumberBaseRoundType.DOWN)
{
v = Math.floor(v);
}
else if (roundType == NumberBaseRoundType.NEAREST)
{
v = Math.round(v);
}
else
{
isValid = false;
return "";
}
// Shift decimal left to get back decimal to original point.
v = v / Math.pow(10, precision);
}
return v.toString();
}
/**
* Formats a number by replacing the default decimal separator, ".",
* with the decimal separator specified by <code>decimalSeparatorTo</code>.
*
* @param value The String value of the Number
* (formatted American style ####.##).
*
* @return String representation of the input where "." is replaced
* with the decimal formatting character.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatDecimal(value:String):String
{
var parts:Array = value.split(".");
return parts.join(decimalSeparatorTo);
}
/**
* Formats a number by using
* the <code>thousandsSeparatorTo</code> property as the thousands separator
* and the <code>decimalSeparatorTo</code> property as the decimal separator.
*
* @param value Value to be formatted.
*
* @return Formatted number.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatThousands(value:String):String
{
var v:Number = Number(value);
var isNegative:Boolean = (v < 0);
var numStr:String = Math.abs(v).toString();
numStr.toLowerCase();
var e:int = numStr.indexOf("e")
if (e != -1) //deal with exponents
numStr = expandExponents(numStr);
var numArr:Array =
numStr.split((numStr.indexOf(decimalSeparatorTo) != -1) ? decimalSeparatorTo : ".");
var numLen:int = String(numArr[0]).length;
if (numLen > 3)
{
var numSep:int = int(Math.floor(numLen / 3));
if ((numLen % 3) == 0)
numSep--;
var b:int = numLen;
var a:int = b - 3;
var arr:Array = [];
for (var i:int = 0; i <= numSep; i++)
{
arr[i] = numArr[0].slice(a, b);
a = int(Math.max(a - 3, 0));
b = int(Math.max(b - 3, 1));
}
arr.reverse();
numArr[0] = arr.join(thousandsSeparatorTo);
}
numStr = numArr.join(decimalSeparatorTo);
if (isNegative)
numStr = "-" + numStr;
return numStr.toString();
}
/**
* Formats a number by setting its decimal precision by using
* the <code>decimalSeparatorTo</code> property as the decimal separator.
*
* @param value Value to be formatted.
*
* @param precision Number of decimal points to use.
*
* @return Formatted number.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatPrecision(value:String, precision:int):String
{
// precision works differently now. Its default value is -1
// which stands for 'do not alter the number's precision'. If a precision
// value is set, all numbers will contain that precision. Otherwise, there
// precision will not be changed.
if (precision == -1)
return value;
var numArr:Array = value.split(decimalSeparatorTo);
numArr[0] = numArr[0].length == 0 ? "0" : numArr[0];
if (precision > 0)
{
var decimalVal:String = numArr[1] ? String(numArr[1]) : "";
var fraction:String =
decimalVal + "000000000000000000000000000000000";
value = numArr[0] + decimalSeparatorTo + fraction.substr(0, precision);
}
else
{
value = String(numArr[0]);
}
return value.toString();
}
/**
* Formats a negative number with either a minus sign (-)
* or parentheses ().
*
* @param value Value to be formatted.
*
* @param useSign If <code>true</code>, use a minus sign (-).
* If <code>false</code>, use parentheses ().
*
* @return Formatted number.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatNegative(value:String, useSign:Boolean):String
{
if (useSign)
{
if (value.charAt(0) != "-")
value = "-" + value;
}
else if (!useSign)
{
if (value.charAt(0) == "-")
value = value.substr(1, value.length - 1);
value = "(" + value + ")";
}
else
{
isValid = false;
return "";
}
return value;
}
/**
* Extracts a number from a formatted String.
* Examines the String from left to right
* and returns the first number sequence.
* Ignores thousands separators and includes the
* decimal and numbers trailing the decimal.
*
* @param str String to parse for the numeric value.
*
* @return Value, which can be a decimal.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function parseNumberString(str:String):String
{
// Check the decimal and thousands formatting for validity.
var splitDec:Array = str.split(decimalSeparatorFrom);
if (splitDec.length > 2)
return null;
// Attempt to extract the first number sequence from the string.
var len:int = str.length;
var count:int = 0;
var letter:String;
var num:String;
var isNegative:Boolean = false;
var hasExponent:Boolean = false
while (count < len)
{
letter = str.charAt(count);
count++;
if (("0" <= letter && letter <= "9") || (letter == decimalSeparatorFrom))
{
var lastLetter:String = str.charAt(count - 2);
// We must handle cases where we have negative input like "-YTL5.000,00"
// and currencySymbol is longer than one character.
if ((lastLetter == "-") || (str.charAt(0) == "-"))
isNegative = true;
num = "";
count--;
for (var i:int = count; i < len; i++)
{
letter = str.charAt(count);
count++;
if ("0" <= letter && letter <= "9")
{
num += letter;
}
else if (letter == decimalSeparatorFrom)
{
num += ".";
}
else if (letter == "e" || letter == "E")
{
num += letter;
hasExponent = true;
}
else if (hasExponent && (letter == "-" || letter == "+"))
{
num += letter;
}
else if (letter != thousandsSeparatorFrom || count >= len)
{
break;
}
}
}
}
// there are all sorts of variations of zero, such as:
// .0 0. 0.0 0000 0000.00000 -0
// Here, we simply convert to a 'real' Number and see if
// it's resolved to zero. if it does, just return a zero
// string and be done with it.
if ((num != null) && (str != ''))
{
var n:Number = Number(num);
if (n == 0)
return '0';
}
// if the last digit is the dot, whack it
if (num)
{
if (num.charAt(num.length-1) == '.')
{
// we have something like 33.
if (num.length >= 2)
{
num = num.substring(0, num.length-1);
}
// we have merely .
else if (num.length == 1)
{
num = '';
isNegative = false;
}
}
}
return isNegative ? "-" + num : num;
}
/**
* Formats a number in exponent notation, into
* a number in decimal notation.
*
* @param str String to process in exponent notation.
*
* @return Formatted number.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function expandExponents(value:String):String
{
//Break string into component parts
var regExp:RegExp = /^([+-])?(\d+).?(\d*)[eE]([-+]?\d+)$/;
var result:Array = regExp.exec(value);
var sign:String = result[1];
var first:String = result[2];
var last:String = result[3];
var exp:int = int(result[4]);
//if we didn't get a good exponent to begin with, return what we can figure out
if (!exp)
{
return (sign ? sign : "") + (first ? first : "0") + (last ? "." + last : "");
}
var r:String = first + last;
var decimal:Boolean = exp < 0;
if (decimal)
{
var o:Number = -1 * (first.length + exp) + 1;
return (sign ? sign : "") + "0." + new Array(o).join("0") + r;
}
else
{
var i:Number = exp + first.length;
if (i >= r.length)
return (sign ? sign : "") + r + new Array(i - r.length + 1).join("0");
else
return (sign ? sign : "") + r.substr(0,i) + "." + r.substr(i);
}
}
}
}