////////////////////////////////////////////////////////////////////////////////
//
// 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.validators
{
import mx.managers.ISystemManager;
import mx.managers.SystemManager;
[ResourceBundle("validators")]
[Alternative(replacement="spark.validators.NumberValidator", since="4.5")]
/**
* The NumberValidator class ensures that a String represents a valid number.
* It can ensure that the input falls within a given range
* (specified by minValue
and maxValue
),
* is an integer (specified by domain
),
* is non-negative (specified by allowNegative
),
* and does not exceed the specified precision
.
* The validator correctly validates formatted numbers (e.g., "12,345.67")
* and you can customize the thousandsSeparator
and
* decimalSeparator
properties for internationalization.
*
* @mxml
*
*
The <mx:NumberValidator>
tag
* inherits all of the tag attributes of its superclass,
* and adds the following tag attributes:
* <mx:NumberValidator * allowNegative="true|false" * decimalPointCountError="The decimal separator can only occur once." * decimalSeparator="." * domain="real|int" * exceedsMaxError="The number entered is too large." * integerError="The number must be an integer." * invalidCharError="The input contains invalid characters." * invalidFormatCharsError="One of the formatting parameters is invalid." * lowerThanMinError="The amount entered is too small." * maxValue="NaN" * minValue="NaN" * negativeError="The amount may not be negative." * precision="-1" * precisionError="The amount entered has too many digits beyond the decimal point." * separationError="The thousands separator must be followed by three digits." * thousandsSeparator="," * /> ** * @includeExample examples/NumberValidatorExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class NumberValidator extends Validator { include "../core/Version.as"; /** * Convenience method for calling a validator * from within a custom validation function. * Each of the standard Flex validators has a similar convenience method. * * @param validator The NumberValidator instance. * * @param value A field to validate. * * @param baseField Text representation of the subfield * specified in the
value
parameter.
* For example, if the value
parameter specifies value.number,
* the baseField
value is "number".
*
* @return An Array of ValidationResult objects, with one ValidationResult
* object for each field examined by the validator.
*
* @see mx.validators.ValidationResult
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static function validateNumber(validator:NumberValidator,
value:Object,
baseField:String):Array
{
var results:Array = [];
// Resource-backed properties of the validator.
var allowNegative:Boolean = validator.allowNegative;
var decimalSeparator:String = validator.decimalSeparator;
var domain:String = validator.domain;
var maxValue:Number = Number(validator.maxValue);
var minValue:Number = Number(validator.minValue);
var precision:int = int(validator.precision);
var thousandsSeparator:String = validator.thousandsSeparator;
var input:String = String(value);
var len:int = input.length;
var isNegative:Boolean = false;
var i:int;
var c:String;
// Make sure the formatting character parameters are unique,
// are not digits or the negative sign,
// and that the separators are one character.
var invalidFormChars:String = DECIMAL_DIGITS + "-";
if (decimalSeparator == thousandsSeparator ||
invalidFormChars.indexOf(decimalSeparator) != -1 ||
invalidFormChars.indexOf(thousandsSeparator) != -1 ||
decimalSeparator.length != 1 ||
thousandsSeparator.length != 1)
{
results.push(new ValidationResult(
true, baseField, "invalidFormatChar",
validator.invalidFormatCharsError));
return results;
}
// Check for invalid characters in input.
var validChars:String = DECIMAL_DIGITS + "-" +
decimalSeparator + thousandsSeparator;
for (i = 0; i < len; i++)
{
c = input.charAt(i);
if (validChars.indexOf(c) == -1)
{
results.push(new ValidationResult(
true, baseField, "invalidChar",
validator.invalidCharError));
return results;
}
}
// Check if the input is negative.
if (input.charAt(0) == "-")
{
if (len == 1) // we have only '-' char
{
results.push(new ValidationResult(
true, baseField, "invalidChar",
validator.invalidCharError));
return results;
}
else if (len == 2 && input.charAt(1) == '.') // handle "-."
{
results.push(new ValidationResult(
true, baseField, "invalidChar",
validator.invalidCharError));
return results;
}
// Check if negative input is allowed.
if (!allowNegative)
{
results.push(new ValidationResult(
true, baseField, "negative",
validator.negativeError));
return results;
}
// Strip off the minus sign, update some variables.
input = input.substring(1);
len--;
isNegative = true;
}
// Make sure there's only one decimal point.
if (input.indexOf(decimalSeparator) !=
input.lastIndexOf(decimalSeparator))
{
results.push(new ValidationResult(
true, baseField, "decimalPointCount",
validator.decimalPointCountError));
return results;
}
// Make sure every character after the decimal is a digit,
// and that there aren't too many digits after the decimal point:
// if domain is int there should be none,
// otherwise there should be no more than specified by precision.
var decimalSeparatorIndex:Number = input.indexOf(decimalSeparator);
if (decimalSeparatorIndex != -1)
{
var numDigitsAfterDecimal:Number = 0;
if (i == 1 && i == len) // we only have a '.'
{
results.push(new ValidationResult(
true, baseField, "invalidChar",
validator.invalidCharError));
return results;
}
for (i = decimalSeparatorIndex + 1; i < len; i++)
{
// This character must be a digit.
if (DECIMAL_DIGITS.indexOf(input.charAt(i)) == -1)
{
results.push(new ValidationResult(
true, baseField, "invalidChar",
validator.invalidCharError));
return results;
}
++numDigitsAfterDecimal;
// There may not be any non-zero digits after the decimal
// if domain is int.
if (domain == NumberValidatorDomainType.INT && input.charAt(i) != "0")
{
results.push(new ValidationResult(
true, baseField,"integer",
validator.integerError));
return results;
}
// Make sure precision is not exceeded.
if (precision != -1 &&
numDigitsAfterDecimal > precision)
{
results.push(new ValidationResult(
true, baseField, "precision",
validator.precisionError));
return results;
}
}
}
// Make sure the input begins with a digit or a decimal point.
if (DECIMAL_DIGITS.indexOf(input.charAt(0)) == -1 &&
input.charAt(0) != decimalSeparator)
{
results.push(new ValidationResult(
true, baseField, "invalidChar",
validator.invalidCharError));
return results;
}
// Make sure that every character before the decimal point
// is a digit or is a thousands separator.
// If it's a thousands separator,
// make sure it's followed by three consecutive digits.
var end:int = decimalSeparatorIndex == -1 ?
len :
decimalSeparatorIndex;
for (i = 1; i < end; i++)
{
c = input.charAt(i);
if (c == thousandsSeparator)
{
if (c == thousandsSeparator)
{
if ((end - i != 4 &&
input.charAt(i + 4) != thousandsSeparator) ||
DECIMAL_DIGITS.indexOf(input.charAt(i + 1)) == -1 ||
DECIMAL_DIGITS.indexOf(input.charAt(i + 2)) == -1 ||
DECIMAL_DIGITS.indexOf(input.charAt(i + 3)) == -1)
{
results.push(new ValidationResult(
true, baseField, "separation",
validator.separationError));
return results;
}
}
}
else if (DECIMAL_DIGITS.indexOf(c) == -1)
{
results.push(new ValidationResult(
true, baseField,"invalidChar",
validator.invalidCharError));
return results;
}
}
// Make sure the input is within the specified range.
if (!isNaN(minValue) || !isNaN(maxValue))
{
// First strip off the thousands separators.
for (i = 0; i < end; i++)
{
if (input.charAt(i) == thousandsSeparator)
{
var left:String = input.substring(0, i);
var right:String = input.substring(i + 1);
input = left + right;
}
}
// Translate the value back into standard english
// If the decimalSeperator is not '.' we need to change it to '.'
// so that the number casting will work properly
if (validator.decimalSeparator != '.')
{
var dIndex:int = input.indexOf( validator.decimalSeparator );
if (dIndex != -1)
{
var dLeft:String = input.substring(0, dIndex);
var dRight:String = input.substring(dIndex + 1);
input = dLeft + '.' + dRight;
}
}
// Check bounds
var x:Number = Number(input);
if (isNegative)
x = -x;
if (!isNaN(minValue) && x < minValue)
{
results.push(new ValidationResult(
true, baseField, "lowerThanMin",
validator.lowerThanMinError));
return results;
}
if (!isNaN(maxValue) && x > maxValue)
{
results.push(new ValidationResult(
true, baseField, "exceedsMax",
validator.exceedsMaxError));
return results;
}
}
return results;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function NumberValidator()
{
super();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// allowNegative
//----------------------------------
/**
* @private
* Storage for the allowNegative property.
*/
private var _allowNegative:Object;
/**
* @private
*/
private var allowNegativeOverride:Object;
[Inspectable(category="General", defaultValue="null")]
/**
* Specifies whether negative numbers are permitted.
* Valid values are true
or false
.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get allowNegative():Object
{
return _allowNegative;
}
/**
* @private
*/
public function set allowNegative(value:Object):void
{
allowNegativeOverride = value;
_allowNegative = value != null ?
Boolean(value) :
resourceManager.getBoolean(
"validators", "allowNegative");
}
//----------------------------------
// decimalSeparator
//----------------------------------
/**
* @private
* Storage for the decimalSeparator property.
*/
private var _decimalSeparator:String;
/**
* @private
*/
private var decimalSeparatorOverride:String;
[Inspectable(category="General", defaultValue="null")]
/**
* The character used to separate the whole
* from the fractional part of the number.
* Cannot be a digit and must be distinct from the
* thousandsSeparator
.
*
* @default "."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get decimalSeparator():String
{
return _decimalSeparator;
}
/**
* @private
*/
public function set decimalSeparator(value:String):void
{
decimalSeparatorOverride = value;
_decimalSeparator = value != null ?
value :
resourceManager.getString(
"validators", "decimalSeparator");
}
//----------------------------------
// domain
//----------------------------------
/**
* @private
* Storage for the domain property.
*/
private var _domain:String;
/**
* @private
*/
private var domainOverride:String;
[Inspectable(category="General", enumeration="int,real", defaultValue="null")]
/**
* Type of number to be validated.
* Permitted values are "real"
and "int"
.
*
* In ActionScript, you can use the following constants to set this property:
* NumberValidatorDomainType.REAL
or
* NumberValidatorDomainType.INT
.
0
has the same effect
* as setting domain
to "int"
.
* A value of -1 means it is ignored.
*
* @default -1
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get precision():Object
{
return _precision;
}
/**
* @private
*/
public function set precision(value:Object):void
{
precisionOverride = value;
_precision = value != null ?
int(value) :
resourceManager.getInt(
"validators", "numberValidatorPrecision");
}
//----------------------------------
// thousandsSeparator
//----------------------------------
/**
* @private
* Storage for the thousandsSeparator property.
*/
private var _thousandsSeparator:String;
/**
* @private
*/
private var thousandsSeparatorOverride:String;
[Inspectable(category="General", defaultValue="null")]
/**
* The character used to separate thousands
* in the whole part of the number.
* Cannot be a digit and must be distinct from the
* decimalSeparator
.
*
* @default ","
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get thousandsSeparator():String
{
return _thousandsSeparator;
}
/**
* @private
*/
public function set thousandsSeparator(value:String):void
{
thousandsSeparatorOverride = value;
_thousandsSeparator = value != null ?
value :
resourceManager.getString(
"validators", "thousandsSeparator");
}
//--------------------------------------------------------------------------
//
// Properties: Errors
//
//--------------------------------------------------------------------------
//----------------------------------
// decimalPointCountError
//----------------------------------
/**
* @private
* Storage for the decimalPointCountError property.
*/
private var _decimalPointCountError:String;
/**
* @private
*/
private var decimalPointCountErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the decimal separator character occurs more than once.
*
* @default "The decimal separator can occur only once."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get decimalPointCountError():String
{
return _decimalPointCountError;
}
/**
* @private
*/
public function set decimalPointCountError(value:String):void
{
decimalPointCountErrorOverride = value;
_decimalPointCountError = value != null ?
value :
resourceManager.getString(
"validators", "decimalPointCountError");
}
//----------------------------------
// exceedsMaxError
//----------------------------------
/**
* @private
* Storage for the exceedsMaxError property.
*/
private var _exceedsMaxError:String;
/**
* @private
*/
private var exceedsMaxErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the value exceeds the maxValue
property.
*
* @default "The number entered is too large."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get exceedsMaxError():String
{
return _exceedsMaxError;
}
/**
* @private
*/
public function set exceedsMaxError(value:String):void
{
exceedsMaxErrorOverride = value;
_exceedsMaxError = value != null ?
value :
resourceManager.getString(
"validators", "exceedsMaxErrorNV");
}
//----------------------------------
// integerError
//----------------------------------
/**
* @private
* Storage for the integerError property.
*/
private var _integerError:String;
/**
* @private
*/
private var integerErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the number must be an integer, as defined
* by the domain
property.
*
* @default "The number must be an integer."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get integerError():String
{
return _integerError;
}
/**
* @private
*/
public function set integerError(value:String):void
{
integerErrorOverride = value;
_integerError = value != null ?
value :
resourceManager.getString(
"validators", "integerError");
}
//----------------------------------
// invalidCharError
//----------------------------------
/**
* @private
* Storage for the invalidCharError property.
*/
private var _invalidCharError:String;
/**
* @private
*/
private var invalidCharErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the value contains invalid characters.
*
* @default The input contains invalid characters."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get invalidCharError():String
{
return _invalidCharError;
}
/**
* @private
*/
public function set invalidCharError(value:String):void
{
invalidCharErrorOverride = value;
_invalidCharError = value != null ?
value :
resourceManager.getString(
"validators", "invalidCharError");
}
//----------------------------------
// invalidFormatCharsError
//----------------------------------
/**
* @private
* Storage for the invalidFormatCharsError property.
*/
private var _invalidFormatCharsError:String;
/**
* @private
*/
private var invalidFormatCharsErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the value contains invalid format characters, which means that
* it contains a digit or minus sign (-) as a separator character,
* or it contains two or more consecutive separator characters.
*
* @default "One of the formatting parameters is invalid."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get invalidFormatCharsError():String
{
return _invalidFormatCharsError;
}
/**
* @private
*/
public function set invalidFormatCharsError(value:String):void
{
invalidFormatCharsErrorOverride = value;
_invalidFormatCharsError = value != null ?
value :
resourceManager.getString(
"validators", "invalidFormatCharsError");
}
//----------------------------------
// lowerThanMinError
//----------------------------------
/**
* @private
* Storage for the lowerThanMinError property.
*/
private var _lowerThanMinError:String;
/**
* @private
*/
private var lowerThanMinErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the value is less than minValue
.
*
* @default "The amount entered is too small."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get lowerThanMinError():String
{
return _lowerThanMinError;
}
/**
* @private
*/
public function set lowerThanMinError(value:String):void
{
lowerThanMinErrorOverride = value;
_lowerThanMinError = value != null ?
value :
resourceManager.getString(
"validators", "lowerThanMinError");
}
//----------------------------------
// negativeError
//----------------------------------
/**
* @private
* Storage for the negativeError property.
*/
private var _negativeError:String;
/**
* @private
*/
private var negativeErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the value is negative and the
* allowNegative
property is false
.
*
* @default "The amount may not be negative."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get negativeError():String
{
return _negativeError;
}
/**
* @private
*/
public function set negativeError(value:String):void
{
negativeErrorOverride = value;
_negativeError = value != null ?
value :
resourceManager.getString(
"validators", "negativeError");
}
//----------------------------------
// precisionError
//----------------------------------
/**
* @private
* Storage for the precisionError property.
*/
private var _precisionError:String;
/**
* @private
*/
private var precisionErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the value has a precision that exceeds the value defined
* by the precision property.
*
* @default "The amount entered has too many digits beyond the decimal point."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get precisionError():String
{
return _precisionError;
}
/**
* @private
*/
public function set precisionError(value:String):void
{
precisionErrorOverride = value;
_precisionError = value != null ?
value :
resourceManager.getString(
"validators", "precisionError");
}
//----------------------------------
// separationError
//----------------------------------
/**
* @private
* Storage for the separationError property.
*/
private var _separationError:String;
/**
* @private
*/
private var separationErrorOverride:String;
[Inspectable(category="Errors", defaultValue="null")]
/**
* Error message when the thousands separator is in the wrong location.
*
* @default "The thousands separator must be followed by three digits."
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get separationError():String
{
return _separationError;
}
/**
* @private
*/
public function set separationError(value:String):void
{
separationErrorOverride = value;
_separationError = value != null ?
value :
resourceManager.getString(
"validators", "separationError");
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function resourcesChanged():void
{
super.resourcesChanged();
allowNegative = allowNegativeOverride;
decimalSeparator = decimalSeparatorOverride;
domain = domainOverride;
maxValue = maxValueOverride;
minValue = minValueOverride;
precision = precisionOverride;
thousandsSeparator = thousandsSeparatorOverride;
decimalPointCountError = decimalPointCountErrorOverride;
exceedsMaxError = exceedsMaxErrorOverride;
integerError = integerErrorOverride;
invalidCharError = invalidCharErrorOverride;
invalidFormatCharsError = invalidFormatCharsErrorOverride;
lowerThanMinError = lowerThanMinErrorOverride;
negativeError = negativeErrorOverride;
precisionError = precisionErrorOverride;
separationError = separationErrorOverride;
}
/**
* Override of the base class doValidation()
method
* to validate a number.
*
* You do not call this method directly; * Flex calls it as part of performing a validation. * If you create a custom Validator class, you must implement this method.
* * @param value Object to validate. * * @return An Array of ValidationResult objects, with one ValidationResult * object for each field examined by the validator. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function doValidation(value:Object):Array { var results:Array = super.doValidation(value); // Return if there are errors // or if the required property is set tofalse
and length is 0.
var val:String = value ? String(value) : "";
if (results.length > 0 || ((val.length == 0) && !required))
return results;
else
return NumberValidator.validateNumber(this, value, null);
}
}
}