//////////////////////////////////////////////////////////////////////////////// // // 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 spark.validators { import flash.globalization.NumberFormatter; import flash.globalization.NumberParseResult; import mx.core.mx_internal; import mx.formatters.IFormatter; import mx.managers.ISystemManager; import mx.managers.SystemManager; import mx.validators.NumberValidatorDomainType; import mx.validators.ValidationResult; import spark.globalization.LastOperationStatus; import spark.validators.supportClasses.NumberValidatorBase; import spark.validators.supportClasses.GlobalizationUtils; import spark.formatters.NumberFormatter; use namespace mx_internal; [ResourceBundle("validators")] /** * The NumberValidator class ensures that a String represents a valid number * according to the conventions of a locale. It can validate strings that * represent int,uint, and Number * objects. * *

This class uses the locale style for specifying the * requested locale ID.

* *

The validator can ensure that the input falls within a given range * (specified by minValue and maxValue properties), * is an integer (specified by domain property), * is non-negative (specified by allowNegative property), * correctly specifies negative and positive numbers, * and does not exceed the specified number offractionalDigits. * The validator sets default property values by making use of the * flash.globalization.NumberFormatter and therefore the locale * specific values are supplied by the operating system.

* *

The NumberValidator class can be used in MXML declarations * or in ActionScript code. This class uses the locale style for specifying the * requested Locale ID required by the * flash.globalization.NumberFormatter class, and has methods and * properties that are bindable.

* * @mxml * *

The <spark:NumberValidator> tag * inherits all of the tag attributes of its superclass, * and adds the following tag attributes:

* *
 *  <s:NumberValidator
 *    Properties
 *    negativeNumberFormat="locale specified string or customized by user."
 *    negativeNumberFormatError="The negative format of the input number is incorrect."
 *  />
 *  
* * @includeExample examples/NumberValidatorExample1.mxml * * @see flash.globalization.NumberFormatter * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public class NumberValidator extends NumberValidatorBase { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class Constants // //-------------------------------------------------------------------------- /** * @private */ private static const NEGATIVE_NUMBER_FORMAT:String = "negativeNumberFormat"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructs a new NumberValidator object to validate numbers according * to the conventions of a given locale. *

* The locale for this class is supplied by the locale style. * The locale style can be set in several ways: *

* *

* If the locale style is not set by one of the above * techniques, the instance of this class will be added as a * StyleClient to the topLevelApplication and * will therefore inherit the locale style from the * topLevelApplication object when the locale * dependent property getter or locale dependent method is * called. *

* * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function NumberValidator() { super(); } //-------------------------------------------------------------------------- // // Overridden Properties // //-------------------------------------------------------------------------- //---------------------------------- // actualLocaleIDName //---------------------------------- [Bindable("change")] /** * * @private */ override public function get actualLocaleIDName():String { return getActualLocaleIDName(NUMBER_VALIDATOR_TYPE); } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // negativeNumberFormat //---------------------------------- [Bindable("change")] /** * A numeric value that indicates a validating pattern for negative * numbers. * This pattern defines the location of the negative symbol * or parentheses in relation to the numeric portion of the * number to be validated. * *

The following table summarizes the possible formats for * negative numbers. When a negative number is validated, * the minus sign represents the value of * the negativeSymbol property and the 'n' character * represents numeric value.

* * * * * * * * * * * * * * * * * * * * * * * * * * *
Negative number format typeFormat
0(n)
1-n
2- n
3n-
4n -
* * * The default value is dependent on the actual locale and * operating system. * * @throws ArgumentError if the assigned value is not a number * between 0 and 4. * * @see #negativeSymbol * @see #format() * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get negativeNumberFormat():uint { return getBasicProperty(properties, NEGATIVE_NUMBER_FORMAT); } public function set negativeNumberFormat(value:uint):void { if (!g11nWorkingInstance) { if (4 < value) throw new TypeError(); } setBasicProperty(properties, NEGATIVE_NUMBER_FORMAT, value); } //-------------------------------------------------------------------------- // // Properties: Errors // //-------------------------------------------------------------------------- //---------------------------------- // negativeNumberFormat error //---------------------------------- /** * @private * Storage for the negativeNumberFormatError property. */ private var _negativeNumberFormatError:String; private var negativeNumberFormatErrorOverride:String; [Inspectable(category="Errors", defaultValue="null")] [Bindable("change")] /** * Error message when the input number's negative number format is not * following the pattern specified by the negativeNumberFormat property. * * @default "The negative format of the input number is incorrect." * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get negativeNumberFormatError():String { return _negativeNumberFormatError; } public function set negativeNumberFormatError(value:String):void { if (negativeNumberFormatErrorOverride && (negativeNumberFormatErrorOverride == value)) { return; } negativeNumberFormatErrorOverride = value; _negativeNumberFormatError = value ? value : resourceManager.getString("validators", "negativeNumberFormatError"); update(); } //-------------------------------------------------------------------------- // // Overridden Methods // //-------------------------------------------------------------------------- /** * @private */ override mx_internal function createWorkingInstance():void { createWorkingInstanceCore(NUMBER_VALIDATOR_TYPE); } /** * @private * * 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 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ 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 to false and length // is 0. var val:String = value ? String(value) : ""; if (results.length > 0 || ((val.length == 0) && !required)) return results; else return validateNumber(value, null); } /** * @private * Load the error messages from the resource bundle. */ override protected function resourcesChanged():void { super.resourcesChanged(); loadChangedResources(); negativeNumberFormatError = negativeNumberFormatErrorOverride; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- [Bindable("change")] /** * Convenience method for calling a validator from within a custom * validation function. Each of the standard Flex validators has a similar * convenience method. Caller must check the ValidationResult * objects in the returned array for validation status. * * @param value A number string to validate. * * @param baseField Text representation of the subfield specified in the * value object. * 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 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function validateNumber(value:Object, baseField:String):Array { const maxValue:Number = Number(maxValue); const minValue:Number = Number(minValue); const inputStr:String = String(value); var results:Array = []; // if the input can be a null/empty, return no error. if (!inputStr) return results; // If spark formatter is null, no-go-forward. If spark formatter locale // id is null or last operationstatus has locale undefined, then also // no forward going situaion. // The spark formatter createion has a situation where it's localeid is // null but LasyOperationStatus is not set. TestLocaleUndef.mxml // testcase has this situation. if ((!g11nWorkingInstance) || (!g11nWorkingInstance.actualLocaleIDName) || (g11nWorkingInstance.lastOperationStatus == LastOperationStatus.LOCALE_UNDEFINED_ERROR)) { results.push(new ValidationResult( true, baseField, "localeUndefinedError", localeUndefinedError)); return results; } // g11nWorkingInstance for a locale is available. // strip leading and trailing white spaces. const input:String = GlobalizationUtils.trim(inputStr); const len:int = input.length; // NumberValidatorBase has this method. Unlike flash globalization, // validator gives error if decimal and grouping separator are same. if (!validateNumberFormat(input, results, baseField)) return results; // Parse the number string using the flash globalization NumberFormatter const nf:spark.formatters.NumberFormatter = g11nWorkingInstance as spark.formatters.NumberFormatter; const inputNum:Number = nf.parseNumber(input); // parseNumber() only returns PARSE_ERROR if it finds any error in input // string. If there is a PARSE_ERROR, validate the input // string further, detect and report the error. parse() is lenient than // parseNumber() and parses to the extent possible without error. Hence // it is not suitable. if (nf.lastOperationStatus == LastOperationStatus.PARSE_ERROR) { if (!detectAndReportProblem(input, baseField, results)) { return results; } else { results.push(new ValidationResult( true, baseField, "parseError", parseError)); return results; } } // See if negative number is allowed. if (!validateNumberNegativity(inputNum, baseField, results)) return results; var dindex:int; if (negativeNumberFormat <= 2) dindex = input.lastIndexOf(decimalSeparator); else dindex = input.indexOf(decimalSeparator); // // 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 fractionalDigits. // if (!validateFractionPart( input, dindex, baseField, results)) { return results; } // Make sure the input is within the specified range. // Make sure the input is within the specified range. // check the range of the number. if (!validateNumberRange(inputNum, baseField, results)) return results; return results; } //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- /** * @private * If there is a parse error by flash.globalization.NumberFormatter, detect * the error and report it for user clarity. parse() method does not return * anything other than PARSE_ERROR. */ private function detectAndReportProblem(input:String, baseField:String, results:Array):Boolean { const len:int = input.length; // Check for invalid characters in input. // One of the negative format of number is enclosing in parenthesis. const validChars:String = VALID_CHARS + decimalSeparator + groupingSeparator; if (validateInputCharacters(input, len, validChars)) { results.push(new ValidationResult( true, baseField, "invalidChar", invalidCharError)); return false; } // Make sure there's only one decimal point. else if ((decimalSeparator != negativeSymbol) && (input.indexOf(decimalSeparator) != input.lastIndexOf(decimalSeparator))) { results.push(new ValidationResult( true, baseField, "decimalPointCount", decimalPointCountError)); return false; } var negPosLeft:Boolean = false; if (negativeNumberFormat <= 2) negPosLeft = true; // NumberValidatorBase has this method. else if (!validateDecimalString(input, baseField, results, negPosLeft)) { return false; } // all errors are already checked. Only remaining error is // negativeNumberFormat. else if ((inputHasNegativeSymbol(input)) || ((input.charAt(0) == "(") && (input.charAt(len-1) == ")"))) { results.push(new ValidationResult( true, baseField, "negativeNumberFormat", negativeNumberFormatError)); return false; } return true; } } }