| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.EventDispatcher; |
| import flash.events.IEventDispatcher; |
| import flash.utils.IDataInput; |
| import flash.utils.IDataOutput; |
| import flash.utils.IExternalizable; |
| import flash.utils.getQualifiedClassName; |
| |
| import mx.core.IPropertyChangeNotifier; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.PropertyChangeEvent; |
| import mx.events.PropertyChangeEventKind; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| import mx.utils.ArrayUtil; |
| import mx.utils.UIDUtil; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when the IList has been updated in some way. |
| * |
| * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="collectionChange", type="mx.events.CollectionEvent")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [RemoteClass(alias="flex.messaging.io.ArrayList")] |
| |
| [ResourceBundle("collections")] |
| |
| [DefaultProperty("source")] |
| |
| /** |
| * The ArrayList class is a simple implementation of IList |
| * that uses a backing Array as the source of the data. |
| * |
| * Items in the backing Array can be accessed and manipulated |
| * using the methods and properties of the <code>IList</code> |
| * interface. Operations on an ArrayList instance modify the |
| * data source; for example, if you use the <code>removeItemAt()</code> |
| * method on an ArrayList, you remove the item from the underlying |
| * Array. |
| * |
| * This base class will not throw ItemPendingErrors but it |
| * is possible that a subclass might. |
| * |
| * <pre> |
| * <mx:ArrayList |
| * <b>Properties</b> |
| * source="null" |
| * /> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public class ArrayList extends EventDispatcher |
| implements IList, IExternalizable, IPropertyChangeNotifier |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Construct a new ArrayList using the specified array as its source. |
| * If no source is specified an empty array will be used. |
| * |
| * @param source The Array to use as a source for the ArrayList. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function ArrayList(source:Array = null) |
| { |
| super(); |
| |
| disableEvents(); |
| this.source = source; |
| enableEvents(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Used for accessing localized Error messages. |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| /** |
| * @private |
| * Indicates if events should be dispatched. |
| * calls to enableEvents() and disableEvents() effect the value when == 0 |
| * events should be dispatched. |
| */ |
| private var _dispatchEvents:int = 0; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // length |
| //---------------------------------- |
| |
| [Bindable("collectionChange")] |
| |
| /** |
| * Get the number of items in the list. An ArrayList should always |
| * know its length so it shouldn't return -1, though a subclass may |
| * override that behavior. |
| * |
| * @return int representing the length of the source. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get length():int |
| { |
| if (source) |
| return source.length; |
| else |
| return 0; |
| } |
| |
| //---------------------------------- |
| // source |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the source Array. |
| */ |
| private var _source:Array; |
| |
| /** |
| * The source array for this ArrayList. |
| * Any changes done through the IList interface will be reflected in the |
| * source array. |
| * If no source array was supplied the ArrayList will create one internally. |
| * Changes made directly to the underlying Array (e.g., calling |
| * <code>theList.source.pop()</code> will not cause <code>CollectionEvents</code> |
| * to be dispatched. |
| * |
| * @return An Array that represents the underlying source. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get source():Array |
| { |
| return _source; |
| } |
| |
| public function set source(s:Array):void |
| { |
| var i:int; |
| var len:int; |
| if (_source && _source.length) |
| { |
| len = _source.length; |
| for (i = 0; i < len; i++) |
| { |
| stopTrackUpdates(_source[i]); |
| } |
| } |
| _source = s ? s : []; |
| len = _source.length; |
| for (i = 0; i < len; i++) |
| { |
| startTrackUpdates(_source[i]); |
| } |
| |
| if (_dispatchEvents == 0) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.RESET; |
| dispatchEvent(event); |
| } |
| } |
| |
| //---------------------------------- |
| // uid -- mx.core.IPropertyChangeNotifier |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the UID String. |
| */ |
| private var _uid:String; |
| |
| /** |
| * Provides access to the unique id for this list. |
| * |
| * @return String representing the internal uid. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get uid():String |
| { |
| if (!_uid) { |
| _uid = UIDUtil.createUID(); |
| } |
| return _uid; |
| } |
| |
| public function set uid(value:String):void |
| { |
| _uid = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Converts an Array List to JavaScript Object Notation (JSON) format. |
| * Called by the JSON.stringify() method and should not be called directly. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3.0 |
| * @productversion Apache Flex 4.12 |
| */ |
| public function toJSON(s:String):* |
| { |
| return toArray(); |
| } |
| |
| /** |
| * Get the item at the specified index. |
| * |
| * @param index the index in the list from which to retrieve the item |
| * @param prefetch int indicating both the direction and amount of items |
| * to fetch during the request should the item not be local. |
| * @return the item at that index, null if there is none |
| * @throws ItemPendingError if the data for that index needs to be |
| * loaded from a remote location |
| * @throws RangeError if the index < 0 or index >= length |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getItemAt(index:int, prefetch:int = 0):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| return source[index]; |
| } |
| |
| /** |
| * Place the item at the specified index. |
| * If an item was already at that index the new item will replace it and it |
| * will be returned. |
| * |
| * @param item the new value for the index |
| * @param index the index at which to place the item |
| * @return the item that was replaced, null if none |
| * @throws RangeError if index is less than 0 or greater than or equal to length |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function setItemAt(item:Object, index:int):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var oldItem:Object = source[index]; |
| source[index] = item; |
| stopTrackUpdates(oldItem); |
| startTrackUpdates(item); |
| |
| //dispatch the appropriate events |
| if (_dispatchEvents == 0) |
| { |
| var hasCollectionListener:Boolean = |
| hasEventListener(CollectionEvent.COLLECTION_CHANGE); |
| var hasPropertyListener:Boolean = |
| hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE); |
| var updateInfo:PropertyChangeEvent; |
| |
| if (hasCollectionListener || hasPropertyListener) |
| { |
| updateInfo = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| updateInfo.kind = PropertyChangeEventKind.UPDATE; |
| updateInfo.oldValue = oldItem; |
| updateInfo.newValue = item; |
| updateInfo.property = index; |
| } |
| |
| if (hasCollectionListener) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REPLACE; |
| event.location = index; |
| event.items.push(updateInfo); |
| dispatchEvent(event); |
| } |
| |
| if (hasPropertyListener) |
| { |
| dispatchEvent(updateInfo); |
| } |
| } |
| return oldItem; |
| } |
| |
| /** |
| * Add the specified item to the end of the list. |
| * Equivalent to addItemAt(item, length); |
| * |
| * @param item the item to add |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addItem(item:Object):void |
| { |
| addItemAt(item, length); |
| } |
| |
| /** |
| * Add the item at the specified index. |
| * Any item that was after this index is moved out by one. |
| * |
| * @param item the item to place at the index |
| * @param index the index at which to place the item |
| * @throws RangeError if index is less than 0 or greater than the length |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addItemAt(item:Object, index:int):void |
| { |
| const spliceUpperBound:int = length; |
| |
| if (index < spliceUpperBound && index > 0) |
| { |
| source.splice(index, 0, item); |
| } |
| else if (index == spliceUpperBound) |
| { |
| source.push(item); |
| } |
| else if (index == 0) |
| { |
| source.unshift(item); |
| } |
| else |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| startTrackUpdates(item); |
| internalDispatchEvent(CollectionEventKind.ADD, item, index); |
| } |
| |
| /** |
| * @copy mx.collections.ListCollectionView#addAll() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addAll(addList:IList):void |
| { |
| addAllAt(addList, length); |
| } |
| |
| /** |
| * @copy mx.collections.ListCollectionView#addAllAt() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addAllAt(addList:IList, index:int):void |
| { |
| const addListLength:int = addList.length; |
| if (addListLength == 0) |
| return; |
| |
| const addedItems:Array = []; |
| |
| disableEvents(); |
| for (var i:int = 0; i < addListLength; i++) |
| { |
| var item:Object = addList.getItemAt(i); |
| this.addItemAt(item, i + index); |
| addedItems.push(item); |
| } |
| enableEvents(); |
| |
| if (_dispatchEvents == 0) |
| { |
| const event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.ADD; |
| event.location = index; |
| event.items = addedItems; |
| dispatchEvent(event); |
| } |
| } |
| |
| /** |
| * Return the index of the item if it is in the list such that |
| * getItemAt(index) == item. |
| * Note that in this implementation the search is linear and is therefore |
| * O(n). |
| * |
| * @param item the item to find |
| * @return the index of the item, -1 if the item is not in the list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getItemIndex(item:Object):int |
| { |
| return ArrayUtil.getItemIndex(item, source); |
| } |
| |
| /** |
| * Removes the specified item from this list, should it exist. |
| * |
| * @param item Object reference to the item that should be removed. |
| * @return Boolean indicating if the item was removed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeItem(item:Object):Boolean |
| { |
| var index:int = getItemIndex(item); |
| var result:Boolean = index >= 0; |
| if (result) |
| removeItemAt(index); |
| |
| return result; |
| } |
| |
| /** |
| * Remove the item at the specified index and return it. |
| * Any items that were after this index are now one index earlier. |
| * |
| * @param index The index from which to remove the item. |
| * @return The item that was removed. |
| * @throws RangeError if index < 0 or index >= length. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeItemAt(index:int):Object |
| { |
| const spliceUpperBound:int = length - 1; |
| var removed:Object; |
| |
| if (index > 0 && index < spliceUpperBound) |
| { |
| removed = source.splice(index, 1)[0]; |
| } |
| else if (index == spliceUpperBound) |
| { |
| removed = source.pop(); |
| } |
| else if (index == 0) |
| { |
| removed = source.shift(); |
| } |
| else |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| stopTrackUpdates(removed); |
| internalDispatchEvent(CollectionEventKind.REMOVE, removed, index); |
| return removed; |
| } |
| |
| /** |
| * Remove all items from the list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeAll():void |
| { |
| if (length > 0) |
| { |
| var len:int = length; |
| for (var i:int = 0; i < len; i++) |
| { |
| stopTrackUpdates(source[i]); |
| } |
| |
| source.splice(0, length); |
| internalDispatchEvent(CollectionEventKind.RESET); |
| } |
| } |
| |
| /** |
| * Notify the view that an item has been updated. |
| * This is useful if the contents of the view do not implement |
| * <code>IEventDispatcher</code>. |
| * If a property is specified the view may be able to optimize its |
| * notification mechanism. |
| * Otherwise it may choose to simply refresh the whole view. |
| * |
| * @param item The item within the view that was updated. |
| * |
| * @param property A String, QName, or int |
| * specifying the property that was updated. |
| * |
| * @param oldValue The old value of that property. |
| * (If property was null, this can be the old value of the item.) |
| * |
| * @param newValue The new value of that property. |
| * (If property was null, there's no need to specify this |
| * as the item is assumed to be the new value.) |
| * |
| * @see mx.events.CollectionEvent |
| * @see mx.core.IPropertyChangeNotifier |
| * @see mx.events.PropertyChangeEvent |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function itemUpdated(item:Object, property:Object = null, |
| oldValue:Object = null, |
| newValue:Object = null):void |
| { |
| var event:PropertyChangeEvent = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| |
| event.kind = PropertyChangeEventKind.UPDATE; |
| event.source = item; |
| event.property = property; |
| event.oldValue = oldValue; |
| event.newValue = newValue; |
| |
| if(!property) |
| { |
| stopTrackUpdates(oldValue); |
| startTrackUpdates(newValue); |
| } |
| |
| itemUpdateHandler(event); |
| } |
| |
| /** |
| * Return an Array that is populated in the same order as the IList |
| * implementation. |
| * |
| * @return An Array populated in the same order as the IList |
| * implementation. |
| * |
| * @throws ItemPendingError if the data is not yet completely loaded |
| * from a remote location |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function toArray():Array |
| { |
| return source.concat(); |
| } |
| |
| /** |
| * Ensures that only the source property is serialized. |
| * @private |
| */ |
| public function readExternal(input:IDataInput):void |
| { |
| source = input.readObject(); |
| } |
| |
| /** |
| * Ensures that only the source property is serialized. |
| * @private |
| */ |
| public function writeExternal(output:IDataOutput):void |
| { |
| output.writeObject(_source); |
| } |
| |
| /** |
| * Pretty prints the contents of this ArrayList to a string and returns it. |
| * |
| * @return A String containing the contents of the ArrayList. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function toString():String |
| { |
| if (source) |
| return source.toString(); |
| else |
| return getQualifiedClassName(this); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Internal Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Enables event dispatch for this list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function enableEvents():void |
| { |
| _dispatchEvents++; |
| if (_dispatchEvents > 0) |
| _dispatchEvents = 0; |
| } |
| |
| /** |
| * Disables event dispatch for this list. |
| * To re-enable events call enableEvents(), enableEvents() must be called |
| * a matching number of times as disableEvents(). |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function disableEvents():void |
| { |
| _dispatchEvents--; |
| } |
| |
| /** |
| * Dispatches a collection event with the specified information. |
| * |
| * @param kind String indicates what the kind property of the event should be |
| * @param item Object reference to the item that was added or removed |
| * @param location int indicating where in the source the item was added. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function internalDispatchEvent(kind:String, item:Object = null, location:int = -1):void |
| { |
| if (_dispatchEvents == 0) |
| { |
| if (hasEventListener(CollectionEvent.COLLECTION_CHANGE)) |
| { |
| var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = kind; |
| if(kind != CollectionEventKind.RESET && kind != CollectionEventKind.REFRESH) |
| event.items.push(item); |
| event.location = location; |
| dispatchEvent(event); |
| } |
| |
| // now dispatch a complementary PropertyChangeEvent |
| if (hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE) && |
| (kind == CollectionEventKind.ADD || kind == CollectionEventKind.REMOVE)) |
| { |
| var objEvent:PropertyChangeEvent = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| objEvent.property = location; |
| if (kind == CollectionEventKind.ADD) |
| objEvent.newValue = item; |
| else |
| objEvent.oldValue = item; |
| dispatchEvent(objEvent); |
| } |
| } |
| } |
| |
| /** |
| * Called when any of the contained items in the list dispatches a |
| * <code>PropertyChangeEvent</code>. |
| * Wraps it in a <code>CollectionEventKind.UPDATE</code> object. |
| * |
| * @param event The event object for the <code>PropertyChangeEvent</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function itemUpdateHandler(event:PropertyChangeEvent):void |
| { |
| internalDispatchEvent(CollectionEventKind.UPDATE, event); |
| // need to dispatch object event now |
| if (_dispatchEvents == 0 && hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE)) |
| { |
| var objEvent:PropertyChangeEvent = PropertyChangeEvent(event.clone()); |
| var index:uint = getItemIndex(event.target); |
| objEvent.property = index.toString() + "." + event.property; |
| dispatchEvent(objEvent); |
| } |
| } |
| |
| /** |
| * If the item is an IEventDispatcher, watch it for updates. |
| * This method is called by the <code>addItemAt()</code> method, |
| * and when the source is initially assigned. |
| * |
| * @param item The item passed to the <code>addItemAt()</code> method. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function startTrackUpdates(item:Object):void |
| { |
| if (item && (item is IEventDispatcher)) |
| { |
| IEventDispatcher(item).addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, itemUpdateHandler, false, 0, true); |
| } |
| } |
| |
| /** |
| * If the item is an IEventDispatcher, stop watching it for updates. |
| * This method is called by the <code>removeItemAt()</code> and |
| * <code>removeAll()</code> methods, and before a new |
| * source is assigned. |
| * |
| * @param item The item passed to the <code>removeItemAt()</code> method. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function stopTrackUpdates(item:Object):void |
| { |
| if (item && item is IEventDispatcher) |
| { |
| IEventDispatcher(item).removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, itemUpdateHandler); |
| } |
| } |
| } |
| } |