| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.collections |
| { |
| |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| |
| import mx.collections.errors.SortError; |
| import mx.core.mx_internal; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| import mx.utils.ObjectUtil; |
| |
| [ResourceBundle("collections")] |
| [Alternative(replacement="spark.collections.SortField", since="4.5")] |
| |
| /** |
| * Provides the sorting information required to establish a sort on a field |
| * or property in a collection view. |
| * |
| * The SortField class is meant to be used with the Sort class. |
| * |
| * Typically the sort is defined for collections of complex items, that is |
| * items in which the sort is performed on properties of those objects. |
| * As in the following example: |
| * |
| * <pre><code> |
| * var col:ICollectionView = new ArrayCollection(); |
| * col.addItem({first:"Anders", last:"Dickerson"}); |
| * var sort:Sort = new Sort(); |
| * sort.fields = [new SortField("first", true)]; |
| * col.sort = sort; |
| * </code></pre> |
| * |
| * There are situations in which the collection contains simple items, like |
| * <code>String</code>, <code>Date</code>, <code>Boolean</code>, etc. |
| * In this case, sorting should be applied to the simple type directly. |
| * When constructing a sort for this situation only a single sort field is |
| * required and should not have a <code>name</code> specified. |
| * For example: |
| * |
| * <pre><code> |
| * var col:ICollectionView = new ArrayCollection(); |
| * col.addItem("California"); |
| * col.addItem("Arizona"); |
| * var sort:Sort = new Sort(); |
| * sort.fields = [new SortField(null, true)]; |
| * col.sort = sort; |
| * </code></pre> |
| * |
| * <p>By default the comparison provided by the SortField class does |
| * not provide correct language specific |
| * sorting for strings. For this type of sorting please see the |
| * <code>spark.collections.Sort</code> and |
| * <code>spark.collections.SortField</code> classes.</p> |
| * |
| * @mxml |
| * |
| * <p>The <code><mx:SortField></code> tag has the following attributes:</p> |
| * |
| * <pre> |
| * <mx:SortField |
| * <b>Properties</b> |
| * caseInsensitive="false" |
| * compareFunction="<em>Internal compare function</em>" |
| * descending="false" |
| * name="null" |
| * numeric="null" |
| * /> |
| * </pre> |
| * |
| * @see mx.collections.ICollectionView |
| * @see mx.collections.Sort |
| * @see spark.collections.Sort |
| * @see spark.collections.SortField |
| |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class SortField extends EventDispatcher implements ISortField |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @param name The name of the property that this field uses for |
| * comparison. |
| * If the object is a simple type, pass <code>null</code>. |
| * @param caseInsensitive When sorting strings, tells the comparator |
| * whether to ignore the case of the values. |
| * @param descending Tells the comparator whether to arrange items in |
| * descending order. |
| * @param numeric Tells the comparator whether to compare sort items as |
| * numbers, instead of alphabetically. |
| * @param sortCompareType Gives an indication to SortField which of the |
| * default compare functions to use. |
| * @param customCompareFunction Use a custom function to compare the |
| * objects based on this SortField. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function SortField(name:String = null, |
| caseInsensitive:Boolean = false, |
| descending:Boolean = false, |
| numeric:Object = null, |
| sortCompareType:String = null, |
| customCompareFunction:Function = null) |
| { |
| super(); |
| |
| _name = name; |
| _caseInsensitive = caseInsensitive; |
| _descending = descending; |
| _numeric = numeric; |
| _sortCompareType = sortCompareType; |
| |
| if(customCompareFunction != null) |
| { |
| compareFunction = customCompareFunction; |
| } |
| else if (updateSortCompareType() == false) |
| { |
| _compareFunction = stringCompare; |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Used for accessing localized Error messages. |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //--------------------------------- |
| // arraySortOnOptions |
| //--------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get arraySortOnOptions():int |
| { |
| if (usingCustomCompareFunction |
| || name == null |
| || _compareFunction == xmlCompare |
| || _compareFunction == dateCompare) |
| { |
| return -1; |
| } |
| var options:int = 0; |
| if (caseInsensitive) options |= Array.CASEINSENSITIVE; |
| if (descending) options |= Array.DESCENDING; |
| if (numeric == true || _compareFunction == numericCompare) options |= Array.NUMERIC; |
| return options; |
| } |
| |
| //--------------------------------- |
| // caseInsensitive |
| //--------------------------------- |
| |
| /** |
| * @private |
| * Storage for the caseInsensitive property. |
| */ |
| private var _caseInsensitive:Boolean; |
| |
| [Inspectable(category="General")] |
| [Bindable("caseInsensitiveChanged")] |
| |
| /** |
| * Specifies whether the sort for this field should be case insensitive. |
| * |
| * @default false |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get caseInsensitive():Boolean |
| { |
| return _caseInsensitive; |
| } |
| |
| /** |
| * @deprecated A future release of Apache Flex SDK will remove this function. Please use the constructor |
| * argument instead. |
| */ |
| mx_internal function setCaseInsensitive(value:Boolean):void |
| { |
| if (value != _caseInsensitive) |
| { |
| _caseInsensitive = value; |
| dispatchEvent(new Event("caseInsensitiveChanged")); |
| } |
| } |
| |
| //--------------------------------- |
| // compareFunction |
| //--------------------------------- |
| |
| /** |
| * @private |
| * Storage for the compareFunction property. |
| */ |
| private var _compareFunction:Function; |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * The function that compares two items during a sort of items for the |
| * associated collection. If you specify a <code>compareFunction</code> |
| * property in an <code>ISort</code> object, Flex ignores any |
| * <code>compareFunction</code> properties of the ISort's |
| * <code>SortField</code> objects. |
| * |
| * <p>The compare function must have the following signature:</p> |
| * |
| * <p><code>function myCompare(a:Object, b:Object):int</code></p> |
| * |
| * <p>This function must return the following values:</p> |
| * |
| * <ul> |
| * <li>-1, if the <code>Object a</code> should appear before the |
| * <code>Object b</code> in the sorted sequence</li> |
| * <li>0, if the <code>Object a</code> equals the |
| * <code>Object b</code></li> |
| * <li>1, if the <code>Object a</code> should appear after the |
| * <code>Object b</code> in the sorted sequence</li> |
| * </ul> |
| * |
| * <p>The default value is an internal compare function that can perform |
| * a string, numeric, or date comparison in ascending or descending order, |
| * with case-sensitive or case-insensitive string comparisons. |
| * Specify your own function only if you need a need a custom comparison |
| * algorithm. This is normally only the case if a calculated field is |
| * used in a display.</p> |
| * |
| * Note if you need language-specific sorting then consider using the |
| * <code>spark.collections.SortField</code> class. |
| * |
| * @see spark.collections.SortField |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get compareFunction():Function |
| { |
| return _compareFunction; |
| } |
| |
| /** |
| * @deprecated A future release of Apache Flex SDK will remove this function. Please use the constructor |
| * argument instead. |
| */ |
| public function set compareFunction(c:Function):void |
| { |
| _compareFunction = c; |
| _usingCustomCompareFunction = (c != null); |
| } |
| |
| //--------------------------------- |
| // descending |
| //--------------------------------- |
| |
| /** |
| * @private |
| * Storage for the descending property. |
| */ |
| private var _descending:Boolean; |
| |
| [Inspectable(category="General")] |
| [Bindable("descendingChanged")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get descending():Boolean |
| { |
| return _descending; |
| } |
| |
| /** |
| * @deprecated A future release of Apache Flex SDK will remove this function. Please use the constructor |
| * argument instead. |
| */ |
| public function set descending(value:Boolean):void |
| { |
| if (_descending != value) |
| { |
| _descending = value; |
| dispatchEvent(new Event("descendingChanged")); |
| } |
| } |
| |
| //--------------------------------- |
| // name |
| //--------------------------------- |
| |
| /** |
| * @private |
| * Storage for the name property. |
| */ |
| private var _name:String; |
| |
| [Inspectable(category="General")] |
| [Bindable("nameChanged")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get name():String |
| { |
| return _name; |
| } |
| |
| /** |
| * @deprecated A future release of Apache Flex SDK will remove this function. Please use the constructor |
| * argument instead. |
| */ |
| public function set name(n:String):void |
| { |
| _name = n; |
| dispatchEvent(new Event("nameChanged")); |
| } |
| |
| //--------------------------------- |
| // numeric |
| //--------------------------------- |
| |
| /** |
| * @private |
| * Storage for the numeric property. |
| */ |
| private var _numeric:Object; |
| |
| [Inspectable(category="General")] |
| [Bindable("numericChanged")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get numeric():Object |
| { |
| return _numeric; |
| } |
| |
| /** |
| * @deprecated A future release of Apache Flex SDK will remove this function. Please use the constructor |
| * argument instead. |
| */ |
| public function set numeric(value:Object):void |
| { |
| if (_numeric != value) |
| { |
| _numeric = value; |
| dispatchEvent(new Event("numericChanged")); |
| } |
| } |
| |
| |
| //--------------------------------- |
| // sortCompareType |
| //--------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _sortCompareType:String = null; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11.8 |
| * @playerversion AIR 3.8 |
| * @productversion Flex 4.11 |
| */ |
| [Bindable("sortCompareTypeChanged")] |
| public function get sortCompareType():String |
| { |
| return _sortCompareType; |
| } |
| |
| /** |
| * @deprecated A future release of Apache Flex SDK will remove this function. Please use the constructor |
| * argument instead. |
| */ |
| public function set sortCompareType(value:String):void |
| { |
| if (_sortCompareType != value) |
| { |
| _sortCompareType = value; |
| dispatchEvent(new Event("sortCompareTypeChanged")); |
| } |
| |
| updateSortCompareType(); |
| } |
| |
| |
| //--------------------------------- |
| // usingCustomCompareFunction |
| //--------------------------------- |
| |
| private var _usingCustomCompareFunction:Boolean; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get usingCustomCompareFunction():Boolean |
| { |
| return _usingCustomCompareFunction; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * A pretty printer for SortField that lists the sort fields and their |
| * options. |
| */ |
| override public function toString():String |
| { |
| return ObjectUtil.toString(this); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function initializeDefaultCompareFunction(obj:Object):void |
| { |
| // if the compare function is not already set then we can set it |
| if (!usingCustomCompareFunction) |
| { |
| if (_sortCompareType) |
| { |
| //Attempt to set the compare function based on the sortCompareType |
| if (updateSortCompareType() == true) |
| { |
| return; |
| } |
| } |
| |
| if (numeric == true) |
| _compareFunction = numericCompare; |
| else if (caseInsensitive || numeric == false) |
| _compareFunction = stringCompare; |
| else |
| { |
| // we need to introspect the data a little bit |
| var value:Object; |
| if (_name) |
| { |
| value = getSortFieldValue(obj); |
| } |
| //this needs to be an == null check because !value will return true |
| //where value == 0 or value == false |
| if (value == null) |
| { |
| value = obj; |
| } |
| |
| var typ:String = typeof(value); |
| switch (typ) |
| { |
| case "string": |
| _compareFunction = stringCompare; |
| break; |
| case "object": |
| if (value is Date) |
| { |
| _compareFunction = dateCompare; |
| } |
| else |
| { |
| _compareFunction = stringCompare; |
| var test:String; |
| try |
| { |
| test = value.toString(); |
| } |
| catch(error2:Error) |
| { |
| } |
| if (!test || test == "[object Object]") |
| { |
| _compareFunction = nullCompare; |
| } |
| } |
| break; |
| case "xml": |
| _compareFunction = xmlCompare; |
| break; |
| case "boolean": |
| case "number": |
| _compareFunction = numericCompare; |
| break; |
| } |
| } // else |
| } // if |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function reverse():void |
| { |
| descending = !descending; |
| } |
| |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11.8 |
| * @playerversion AIR 3.8 |
| * @productversion Flex 4.11 |
| */ |
| public function updateSortCompareType():Boolean |
| { |
| if (!_sortCompareType) |
| { |
| return false; |
| } |
| |
| |
| //Lookup the sortCompareType by its SortFieldCompareTypes value and set the associated compare method. |
| switch(_sortCompareType) |
| { |
| case SortFieldCompareTypes.DATE: |
| { |
| _compareFunction = dateCompare; |
| |
| return true; |
| } |
| |
| case SortFieldCompareTypes.NULL: |
| { |
| _compareFunction = nullCompare; |
| |
| return true; |
| } |
| |
| case SortFieldCompareTypes.NUMERIC: |
| { |
| _compareFunction = numericCompare; |
| |
| return true; |
| } |
| |
| case SortFieldCompareTypes.STRING: |
| { |
| _compareFunction = stringCompare; |
| |
| return true; |
| } |
| |
| case SortFieldCompareTypes.XML: |
| { |
| _compareFunction = xmlCompare; |
| |
| return true; |
| } |
| } |
| |
| |
| return false; |
| } |
| |
| |
| public function objectHasSortField(object:Object):Boolean |
| { |
| return getSortFieldValue(object) !== undefined; |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Protected Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| protected function getSortFieldValue(obj:Object):* |
| { |
| var result:* = undefined; |
| |
| try |
| { |
| result = obj[_name]; |
| } |
| catch(error:Error) |
| { |
| } |
| |
| return result; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Private Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| private function nullCompare(a:Object, b:Object):int |
| { |
| var left:Object; |
| var right:Object; |
| |
| var found:Boolean = false; |
| |
| // return 0 (ie equal) if both are null |
| if (a == null && b == null) |
| { |
| return 0; |
| } |
| |
| // we need to introspect the data a little bit |
| if (_name) |
| { |
| left = getSortFieldValue(a); |
| right = getSortFieldValue(b); |
| } |
| |
| // return 0 (ie equal) if both are null |
| if (left == null && right == null) |
| return 0; |
| |
| if (left == null && !_name) |
| left = a; |
| |
| if (right == null && !_name) |
| right = b; |
| |
| |
| var typeLeft:String = typeof(left); |
| var typeRight:String = typeof(right); |
| |
| |
| if (typeLeft == "string" || typeRight == "string") |
| { |
| found = true; |
| _compareFunction = stringCompare; |
| } |
| else if (typeLeft == "object" || typeRight == "object") |
| { |
| if (left is Date || right is Date) |
| { |
| found = true; |
| _compareFunction = dateCompare; |
| } |
| } |
| else if (typeLeft == "xml" || typeRight == "xml") |
| { |
| found = true; |
| _compareFunction = xmlCompare; |
| } |
| else if (typeLeft == "number" || typeRight == "number" |
| || typeLeft == "boolean" || typeRight == "boolean") |
| { |
| found = true; |
| _compareFunction = numericCompare; |
| } |
| |
| if (found) |
| { |
| return _compareFunction(left, right); |
| } |
| else |
| { |
| var message:String = resourceManager.getString( |
| "collections", "noComparatorSortField", [ name ]); |
| throw new SortError(message); |
| } |
| } |
| |
| /** |
| * Pull the numbers from the objects and call the implementation. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function numericCompare(a:Object, b:Object):int |
| { |
| var fa:Number = _name == null ? Number(a) : Number(getSortFieldValue(a)); |
| var fb:Number = _name == null ? Number(b) : Number(getSortFieldValue(b)); |
| |
| return ObjectUtil.numericCompare(fa, fb); |
| } |
| |
| /** |
| * Pull the date objects from the values and compare them. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function dateCompare(a:Object, b:Object):int |
| { |
| var fa:Date = _name == null ? a as Date : getSortFieldValue(a) as Date; |
| var fb:Date = _name == null ? b as Date : getSortFieldValue(b) as Date; |
| |
| return ObjectUtil.dateCompare(fa, fb); |
| } |
| |
| /** |
| * Pull the strings from the objects and call the implementation. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function stringCompare(a:Object, b:Object):int |
| { |
| var fa:String = _name == null ? String(a) : String(getSortFieldValue(a)); |
| var fb:String = _name == null ? String(b) : String(getSortFieldValue(b)); |
| |
| return ObjectUtil.stringCompare(fa, fb, _caseInsensitive); |
| } |
| |
| /** |
| * Pull the values out fo the XML object, then compare |
| * using the string or numeric comparator depending |
| * on the numeric flag. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function xmlCompare(a:Object, b:Object):int |
| { |
| var sa:String = _name == null ? a.toString() : getSortFieldValue(a).toString(); |
| var sb:String = _name == null ? b.toString() : getSortFieldValue(b).toString(); |
| |
| if (numeric == true) |
| { |
| return ObjectUtil.numericCompare(parseFloat(sa), parseFloat(sb)); |
| } |
| else |
| { |
| return ObjectUtil.stringCompare(sa, sb, _caseInsensitive); |
| } |
| } |
| } |
| } |