You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by yi...@apache.org on 2020/10/18 10:36:30 UTC
[royale-asjs] branch develop updated: Add AsyncLisrView (not tested)
This is an automated email from the ASF dual-hosted git repository.
yishayw pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
The following commit(s) were added to refs/heads/develop by this push:
new 1e9feab Add AsyncLisrView (not tested)
1e9feab is described below
commit 1e9feab4a4cda5d2e8530759f0934781192db361
Author: Yishay Weiss <yi...@yell.com>
AuthorDate: Sun Oct 18 11:36:09 2020 +0100
Add AsyncLisrView (not tested)
---
.../MXRoyale/src/main/royale/MXRoyaleClasses.as | 1 +
.../main/royale/mx/collections/AsyncListView.as | 1012 ++++++++++++++++++++
2 files changed, 1013 insertions(+)
diff --git a/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as b/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as
index 6b9c6a0..4b6e665 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as
@@ -162,6 +162,7 @@ internal class MXRoyaleClasses
import mx.skins.ProgrammaticSkin; ProgrammaticSkin;
import mx.rpc.soap.WebService; WebService;
import mx.collections.ISort; ISort;
+ import mx.collections.AsyncListView; AsyncListView;
import mx.utils.Base64Encoder; Base64Encoder;
import mx.utils.Base64Decoder; Base64Decoder;
import mx.utils.BitFlagUtil; BitFlagUtil;
diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/collections/AsyncListView.as b/frameworks/projects/MXRoyale/src/main/royale/mx/collections/AsyncListView.as
new file mode 100644
index 0000000..7281518
--- /dev/null
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/collections/AsyncListView.as
@@ -0,0 +1,1012 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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 org.apache.royale.events.Event;
+import org.apache.royale.events.EventDispatcher;
+import org.apache.royale.reflection.getQualifiedClassName;
+
+import mx.collections.errors.ItemPendingError;
+import mx.core.mx_internal;
+import mx.events.CollectionEvent;
+import mx.events.CollectionEventKind;
+import mx.events.PropertyChangeEvent;
+import mx.events.PropertyChangeEventKind;
+
+use namespace mx_internal; // for mx_internal functions pendingItemSucceeded,Failed()
+
+/**
+ * Dispatched when the list's length has changed or when a list
+ * element is replaced.
+ *
+ * @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")]
+
+/**
+ * The AsyncListView class is an implementation of the IList interface
+ * that handles ItemPendingErrors errors
+ * thrown by the <code>getItemAt()</code>, <code>removeItemAt()</code>,
+ * and <code>toArray()</code> methods.
+ *
+ * <p>The <code>getItemAt()</code> method handles ItemPendingErrors by returning a provisional
+ * "pending" item until the underlying request succeeds or fails. The provisional
+ * item is produced by calling the function specified by the <code>createPendingItemFunction</code>
+ * property. . If the request
+ * succeeds, the actual item replaces the provisional one.
+ * If it fails, the provisional item is replaced with the item returned by calling
+ * the function specified by the <code>createFailedItemFunction</code> property.</p>
+ *
+ * <p>This class delegates the IList methods and properties to its <code>list</code>.
+ * If a list isn't specified, methods that mutate the collection are no-ops,
+ * and methods that query the collection return an empty value, such as null or zero
+ * as appropriate.</p>
+ *
+ * <p>This class is intended to be used with Spark components based on DataGroup,
+ * such as List and ComboBox. The Spark classes do not provide intrinsic support for
+ * ItemPendingError handling.</p>
+ *
+ * <p>AsyncListView does not support re-insertion of pending or failed items. Once
+ * a failed or pending item is removed, its connection to a pending request for data
+ * is lost. Using drag and drop to move a pending item in an ASyncListView, or sorting
+ * an ASyncListView that contains pending or failed items, is not supported because
+ * these operations remove and then re-insert list items.</p>
+ *
+ * @mxml
+ *
+ * <p>The <code><mx:AsyncListView></code> tag inherits all the attributes of its
+ * superclass, and adds the following attributes:</p>
+ *
+ * <pre>
+ * <mx:AsyncListView
+ * <b>Properties</b>
+ * createFailedItemFunction="null"
+ * createPendingItemFunction="null"
+ * list="null"
+ * />
+ * </pre>
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+public class AsyncListView extends EventDispatcher implements IList
+{
+ /**
+ * Constructor.
+ *
+ * @param list Initial value of the list property, the IList we're delegating to.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function AsyncListView(list:IList = null)
+ {
+ super();
+ this.list = list;
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Properties
+ //
+ //--------------------------------------------------------------------------
+
+ //----------------------------------
+ // list
+ //----------------------------------
+
+ private var _list:IList;
+
+ [Inspectable(category="General")]
+ [Bindable("listChanged")]
+
+ /**
+ * The IList object that this collection wraps. That means the object to which all of
+ * the IList methods are delegated.
+ *
+ * <p>If this property is null, the IList mutation methods, such as <code>setItemAt()</code>,
+ * are no-ops. The IList query methods, such <code>getItemAt()</code>, return null
+ * or zero (-1 for <code>getItemIndex()</code>), as appropriate.</p>
+ *
+ * @default null
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get list():IList
+ {
+ return _list;
+ }
+
+ /**
+ * @private
+ */
+ public function set list(value:IList):void
+ {
+ if (_list == value)
+ return;
+
+ deleteAllPendingResponders();
+ oldLength = -1;
+ if (_list)
+ _list.removeEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChangeEvent);
+ _list = value;
+ if (_list)
+ {
+ _list.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChangeEvent);
+ oldLength = _list.length;
+ }
+
+ dispatchEvent(new Event("listChanged"));
+ dispatchEvent(new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.RESET));
+ }
+
+ /**
+ * @private
+ */
+ private function deleteAllPendingResponders():void
+ {
+ for each (var responder:ListItemResponder in pendingResponders)
+ {
+ if (responder)
+ responder.index = -1;
+ }
+ pendingResponders.length = 0;
+ failedItems.length = 0;
+ }
+
+ /**
+ * The previous known length of the list before handling a CollectionEvent.
+ * oldLength is updated by the list setter and isValidCollectionEvent().
+ */
+ private var oldLength:int = -1;
+
+ /**
+ * This method checks the validity of incoming CollectionEvents.
+ * In some cases, a CollectionEvent from the underlying list may have already
+ * been received once, or have been erroneously dispatched (See SDK-30594).
+ * Thus, we check the incoming event's location against the last known length'
+ * of the list (oldLength).
+ *
+ * <p>Returns false if the index is less than 0 or greater than the previous
+ * length of the list.
+ * This only applies to ADD, REMOVE, REPLACE, and MOVE CollectionEvents.
+ * It also updates oldLength to be the current length of the list, so it
+ * should not be called twice.</p>
+ */
+ private function isValidCollectionEvent(ce:CollectionEvent):Boolean
+ {
+ if (oldLength < 0)
+ return true;
+
+ const location:int = ce.location;
+
+ switch (ce.kind)
+ {
+ case CollectionEventKind.ADD:
+ {
+ if (location < 0 || location > oldLength)
+ return false;
+ break;
+ }
+
+ case CollectionEventKind.REMOVE:
+ case CollectionEventKind.REPLACE:
+ case CollectionEventKind.MOVE:
+ {
+ if (location < 0 || location >= oldLength)
+ return false;
+ break;
+ }
+ }
+
+ oldLength = length;
+ return true;
+ }
+
+ /**
+ * @private
+ * Fixup the pendingResponders and failedItems arrays after a change to the list.
+ * Generally speaking, if a list[index] item changes, the pending responder for
+ * that index is no longer needed.
+ *
+ * All "collectionChange" events are redispatched to the AsyncListView listeners.
+ */
+ private function handleCollectionChangeEvent(ce:CollectionEvent):void
+ {
+ if (!isValidCollectionEvent(ce))
+ return;
+
+ switch (ce.kind)
+ {
+ case CollectionEventKind.REPLACE:
+ case CollectionEventKind.UPDATE:
+ deletePendingResponders(ce);
+ break;
+
+ case CollectionEventKind.MOVE:
+ movePendingResponders(ce);
+ break;
+
+ case CollectionEventKind.ADD:
+ shiftPendingRespondersRight(ce);
+ break;
+
+ case CollectionEventKind.REMOVE:
+ shiftPendingRespondersLeft(ce);
+ break;
+
+ case CollectionEventKind.RESET:
+ case CollectionEventKind.REFRESH:
+ deleteAllPendingResponders();
+ break;
+ }
+
+ dispatchEvent(ce); // redispatch to CollectionEvent listeners on this
+ }
+
+ /**
+ * @private
+ * Delete the ListItemResponder at the specified index, if any.
+ * If a pending responder exists, return its item.
+ *
+ * This method assumes that the responder hasn't run yet, it sets
+ * the ListItemResponder index to -1 to prevent it from updating
+ * this AsyncListView later.
+ */
+ private function deletePendingResponder(index:int):Object
+ {
+ if ((index < 0) || (index >= pendingResponders.length))
+ return null;
+
+ const pendingResponder:ListItemResponder = pendingResponders[index];
+ if (pendingResponder)
+ {
+ delete pendingResponders[index];
+ ListItemResponder(pendingResponder).index = -1;
+ return pendingResponder.item;
+ }
+
+ return null;
+ }
+
+ /**
+ * @private
+ * Handler for a CollectionEventKind.UPDATE or REPLACE event. In either
+ * case a contiguous block of items (ce.items) beginning with index=ce.location
+ * has been changed. If there are any pending requests for these indices, we
+ * assume they're no longer valid, i.e. we assume that getItemAt() should no longer
+ * return the pending item. Likewise for failed items.
+ */
+ private function deletePendingResponders(ce:CollectionEvent):void
+ {
+ var index:int = ce.location;
+ for each (var item:Object in ce.items)
+ {
+ deletePendingResponder(index);
+ delete failedItems[index];
+ index += 1;
+ }
+ }
+
+ /**
+ * @private
+ * Handler for a CollectionEventKind.MOVE event. The event indicates that a
+ * contiguous block of items (ce.items), beginning with index=ce.oldLocation,
+ * has been moved to ce.location. If pendingRequests already exist at ce.location,
+ * they're deleted first.
+ */
+ private function movePendingResponders(ce:CollectionEvent):void
+ {
+ var fromIndex:int = ce.oldLocation;
+ var toIndex:int = ce.location;
+ for each (var item:Object in ce.items)
+ {
+ var pendingResponder:ListItemResponder = pendingResponders[fromIndex];
+ if (pendingResponder)
+ {
+ delete pendingResponders[fromIndex];
+ ListItemResponder(pendingResponder).index = toIndex;
+ deletePendingResponder(toIndex); // in case we're copying over a pending request
+ pendingResponders[toIndex] = pendingResponder;
+ }
+
+ var failedItem:* = failedItems[fromIndex];
+ if (failedItem !== undefined)
+ {
+ delete failedItems[fromIndex];
+ failedItems[toIndex] = failedItem;
+ }
+
+ fromIndex += 1;
+ toIndex += 1;
+ }
+ }
+
+ /**
+ * @private
+ * Handler for a CollectionEventKind.ADD. The event indicates
+ * that a block of ce.items.length items starting at ce.location was inserted,
+ * which implies that all of the pendingResponders whose index is greater than or
+ * equal to ce.location, must be shifted right by ce.items.length. The failedItems
+ * array is handled similarly.
+ */
+ private function shiftPendingRespondersRight(ce:CollectionEvent):void
+ {
+ const delta:int = ce.items.length;
+ const startIndex:int = ce.location;
+
+ const pendingRespondersCopy:Array = sparseCopy(pendingResponders);
+ pendingResponders.length = 0;
+ for each (var responder:ListItemResponder in pendingRespondersCopy)
+ {
+ if (responder.index >= startIndex)
+ responder.index += delta;
+ pendingResponders[responder.index] = responder;
+ }
+
+ for (var index:int = failedItems.length - 1; index >= startIndex; index--)
+ {
+ var failedItem:* = failedItems[index];
+ if (failedItem !== undefined)
+ {
+ delete failedItems[index];
+ failedItems[index + delta] = failedItem;
+ }
+ }
+ }
+
+ /**
+ * @private
+ * Handler for a CollectionEventKind.REMOVE. The event indicates
+ * that a block of ce.items.length items starting at ce.location was removed,
+ * which implies that all of the pendingResponders whose index is greater than or
+ * equal to ce.location, must be shifted left by ce.items.length. The failedItems
+ * array is handled similarly.
+ */
+ private function shiftPendingRespondersLeft(ce:CollectionEvent):void
+ {
+ const delta:int = ce.items.length;
+ const startIndex:int = ce.location + delta;
+
+ const pendingRespondersCopy:Array = sparseCopy(pendingResponders);
+ pendingResponders.length = 0;
+ for each (var responder:ListItemResponder in pendingRespondersCopy)
+ {
+ if (responder.index >= startIndex)
+ responder.index -= delta;
+ pendingResponders[responder.index] = responder;
+ }
+
+ const failedItemsLength:int = failedItems.length;
+ for (var index:int = startIndex; index < failedItemsLength; index++)
+ {
+ var failedItem:* = failedItems[index];
+ if (failedItem !== undefined)
+ {
+ delete failedItems[index];
+ failedItems[index - delta] = failedItem;
+ }
+ }
+ }
+
+ /**
+ * Applying concat() to a sparse array produces a new array that's
+ * not sparse, nulls replace items that were undefined. Although the
+ * result of this method is not sparse, it only includes items that
+ * were in the original array.
+ */
+ private function sparseCopy(a:Array):Array
+ {
+ const r:Array = [];
+ var index:int = 0;
+ for each (var item:* in a)
+ {
+ if (item !== undefined)
+ r[index++] = item;
+ }
+ return r;
+ }
+
+ //----------------------------------
+ // createPendingItemFunction
+ //----------------------------------
+
+ private var _createPendingItemFunction:Function = defaultCreatePendingItemFunction;
+
+ /**
+ * @private
+ */
+ private function defaultCreatePendingItemFunction(index:int, ipe:ItemPendingError):Object
+ {
+ return null;
+ }
+
+ /**
+ * A callback function used to create a provisional item when
+ * the initial request causes an <code>ItemPendingError</code> to be thrown.
+ * If the request eventually succeeds, the provisional item is automatically
+ * replaced by the actual item. If the request fails, then the item is replaced
+ * with one created with the callback function specified by the
+ * <code>createFailedItemFunction</code> property.
+ *
+ * <p>The value of this property must be a function with two parameters: the index
+ * of the requested data provider item, and the ItemPendingError itself. In most
+ * cases, the second parameter can be ignored.
+ * The following example shows an implementation of the callback function:
+ *
+ * <pre>
+ * function createPendingItem(index:int, ipe:ItemPendingError):Object
+ * {
+ * return "[" + index + "request is pending...]";
+ * }
+ * </pre>
+ * </p>
+ *
+ * <p>Setting this property does not affect provisional pending items that were already
+ * created. Setting this property to null prevents provisional pending items
+ * from being created.</p>
+ *
+ * @default A function that unconditionally returns null.
+ * @see #getItemAt()
+ * @see #createFailedItemFunction
+ * @see mx.collections.errors.ItemPendingError
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get createPendingItemFunction():Function
+ {
+ return _createPendingItemFunction;
+ }
+
+ /**
+ * @private
+ */
+ public function set createPendingItemFunction(value:Function):void
+ {
+ _createPendingItemFunction = value;
+ }
+
+
+ //----------------------------------
+ // createFailedItemFunction
+ //----------------------------------
+
+ private var _createFailedItemFunction:Function = defaultCreateFailedItemFunction;
+
+ /**
+ * @private
+ */
+ private function defaultCreateFailedItemFunction(index:int, info:Object):Object
+ {
+ return null;
+ }
+
+ /**
+ * A callback function used to create a substitute item when
+ * a request which had caused an <code>ItemPendingError</code> to be thrown,
+ * subsequently fails. The existing item, typically a pending item created
+ * by the callback function specified by the <code>createPendingItemFunction()</code> property,
+ * is replaced with the failed item.
+ *
+ * <p>The value of this property must be a function with two parameters: the index
+ * of the requested item, and the failure "info" object, which is
+ * passed along from the IResponder <code>fault()</code> method.
+ * In most cases you can ignore the second parameter.
+ * Shown below is an example implementation of the callback function:</p>
+ *
+ * <pre>
+ * function createFailedItem(index:int, info:Object):Object
+ * {
+ * return "[" + index + "request failed]";
+ * }
+ * </pre>
+ *
+ *
+ * <p>Setting this property does not affect failed items that were already
+ * created. Setting this property to null prevents failed items from being created.
+ * </p>
+ *
+ * @default A function that unconditionally returns null.
+ * @see #getItemAt()
+ * @see #createPendingItemFunction
+ * @see mx.rpc.IResponder#fault
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get createFailedItemFunction():Function
+ {
+ return _createFailedItemFunction;
+ }
+
+ /**
+ * @private
+ */
+ public function set createFailedItemFunction(value:Function):void
+ {
+ _createFailedItemFunction = value;
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Methods
+ //
+ //--------------------------------------------------------------------------
+
+ private const pendingResponders:Array = [];
+ private const failedItems:Array = [];
+
+ /**
+ * @private
+ * Called by the ListItemProvider/result() method when a pending request
+ * completes successfully.
+ *
+ * @param index The item's index.
+ * @param info The informational object passed to IResponder/result().
+ * @see mx.rpc.IResponder#result
+ */
+ mx_internal function pendingRequestSucceeded(index:int, info:Object):void
+ {
+ delete pendingResponders[index];
+ }
+
+ /**
+ * @private
+ * Called by the ListItemProvider/fault() method when a pending request
+ * fails.
+ *
+ * @param index The item's index.
+ * @param info The informational object passed to IResponder/fault().
+ * @see mx.rpc.IResponder#fault
+ */
+ mx_internal function pendingRequestFailed(index:int, info:Object):void
+ {
+ delete pendingResponders[index];
+
+ if (createFailedItemFunction === null)
+ return;
+
+ const item:Object = createFailedItemFunction(index, info);
+ failedItems[index] = item;
+
+ // dispatch collection and property change events
+
+ const hasCollectionListener:Boolean = hasEventListener(CollectionEvent.COLLECTION_CHANGE);
+ const hasPropertyListener:Boolean = hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE);
+ var pce:PropertyChangeEvent;
+
+ if (hasCollectionListener || hasPropertyListener)
+ {
+ pce = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE);
+ pce.kind = PropertyChangeEventKind.UPDATE;
+ pce.oldValue = null;
+ pce.newValue = item;
+ pce.property = index;
+ }
+
+ if (hasCollectionListener)
+ {
+ var ce:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
+ ce.kind = CollectionEventKind.REPLACE;
+ ce.location = index;
+ ce.items.push(pce);
+ dispatchEvent(ce);
+ }
+
+ if (hasPropertyListener)
+ dispatchEvent(pce);
+ }
+
+
+ //--------------------------------------------------------------------------
+ //
+ // IList Implementation
+ //
+ //--------------------------------------------------------------------------
+
+ [Bindable("collectionChange")]
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function get length():int
+ {
+ try
+ {
+ return (list) ? list.length : 0;
+ }
+ catch (ignore:ItemPendingError)
+ {
+ // The mx.data DataList class can throw an IPE here. We ignore it because
+ // when the length is determined, a CollectionChanged event will be
+ // be dispatched. See handleCollectionChangeEvent().
+ }
+
+ return 0;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function addItem(item:Object):void
+ {
+ if (list)
+ {
+ try
+ {
+ list.addItem(item);
+ }
+ catch (ignore:ItemPendingError)
+ {
+ // The mx.data DataList class can throw an IPE here. We ignore it because
+ // when the item is actually added, a CollectionChanged event will be
+ // be dispatched. See handleCollectionChangeEvent().
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function addItemAt(item:Object, index:int):void
+ {
+ if (list)
+ {
+ try
+ {
+ list.addItemAt(item, index);
+ }
+ catch (ignore:ItemPendingError)
+ {
+ // The mx.data DataList class can throw an IPE here. We ignore it because
+ // when the item is actually added, a CollectionChanged event will be
+ // be dispatched. See handleCollectionChangeEvent().
+ }
+ }
+ }
+
+ /**
+ * Returns the value of <code>list.getItemAt(index)</code>.
+ *
+ * <p>This method catches ItemPendingErrors (IPEs) generated as a consequence of
+ * calling <code>getItemAt()</code>. If an IPE is thrown, an <code>IResponder</code> is added to
+ * the IPE and a provisional "pending" item, created with the
+ * <code>createPendingItemFunction</code> is returned. If the underlying request
+ * eventually succeeds, the pending item is replaced with the real item. If it fails,
+ * the pending item is replaced with a value produced by calling
+ * <code>createFailedItemFunction</code>.</p>
+ *
+ * @param index The list index from which to retrieve the item.
+ *
+ * @param prefetch An <code>int</code> indicating both the direction
+ * and number of items to fetch during the request if the item is not local.
+ *
+ * @throws RangeError if <code>index < 0</code> or <code>index >= length</code>.
+ *
+ * @return The list item at the specified index.
+ *
+ * @see #createPendingItemFunction
+ * @see #createFailedItemFunction
+ * @see mx.collections.errors.ItemPendingError
+ * @see mx.rpc.IResponder
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function getItemAt(index:int, prefetch:int=0):Object
+ {
+ if (!list)
+ return null;
+
+ const failedItem:* = failedItems[index];
+ if (failedItem !== undefined)
+ return failedItem;
+
+ const pendingResponder:ListItemResponder = pendingResponders[index];
+ if (pendingResponder)
+ return pendingResponder.item;
+
+ var item:Object = null;
+ try
+ {
+ return list.getItemAt(index, prefetch);
+ }
+ catch (ipe:ItemPendingError)
+ {
+ const createPendingItem:Function = createPendingItemFunction;
+ if (createPendingItem !== null)
+ item = createPendingItem(index, ipe);
+ var responder:ListItemResponder = new ListItemResponder(this, index, item);
+ pendingResponders[index] = responder;
+ ipe.addResponder(responder);
+ }
+ return item;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function getItemIndex(item:Object):int
+ {
+ const failedItemIndex:int = failedItems.indexOf(item);
+ if (failedItemIndex != -1)
+ return failedItemIndex;
+
+ for each (var responder:ListItemResponder in pendingResponders)
+ if (responder && responder.item === item)
+ return responder.index;
+ return (list) ? list.getItemIndex(item) : -1;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function itemUpdated(item:Object, property:Object=null, oldValue:Object=null, newValue:Object=null):void
+ {
+ if (list)
+ list.itemUpdated(item, property, oldValue, newValue);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function removeAll():void
+ {
+ if (list)
+ list.removeAll();
+ }
+
+ /**
+ * Removes the specified item from this list, should it exist.
+ * Relies on ArrayList implementation
+ *
+ * @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 Apache Flex 4.10
+ */
+ public function removeItem(item:Object):Boolean
+ {
+ var _item:Object = removeItemAt(getItemIndex(item));
+ return _item != null;
+ }
+
+ /**
+ * Removes the actual or pending item at the specified index and returns it.
+ * All items whose index is greater than the specified index
+ * have their index reduced by 1.
+ *
+ * <p>If there is no actual or pending item at the specified index, for
+ * example because a call to <code>getItemAt(index)</code> hasn't caused the data to be
+ * paged in, then the underlying <code>list</code> may throw an ItemPendingError.
+ * The implementation ignores the ItemPendingError and returns null.</p>
+ *
+ * @param index The list index from which to retrieve the item.
+ *
+ * @throws RangeError if <code>index < 0</code> or <code>index >= length</code>.
+ *
+ * @return The item that was removed or null.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function removeItemAt(index:int):Object
+ {
+ if (!list)
+ return null;
+
+ const failedItem:* = failedItems[index];
+ delete failedItems[index];
+ const pendingItem:Object = deletePendingResponder(index);
+ try
+ {
+ const actualItem:Object = list.removeItemAt(index);
+ if (failedItem !== undefined)
+ return failedItem;
+ return (pendingItem) ? pendingItem : actualItem;
+
+ }
+ catch (ipe:ItemPendingError)
+ {
+ // If list[index] doesn't exist yet, an IPE will be thrown. There's nothing
+ // we can do about that, so ignore it.
+ }
+ return (failedItem !== undefined) ? failedItem : pendingItem;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function setItemAt(item:Object, index:int):Object
+ {
+ if (!list)
+ return null;
+
+ const failedItem:* = failedItems[index];
+ const pendingResponder:ListItemResponder = pendingResponders[index];
+
+ var setItemValue:Object = null; // return null if IPE
+ try
+ {
+ setItemValue = list.setItemAt(item, index);
+ }
+ catch (ignore:ItemPendingError)
+ {
+ // The mx.data DataList class can throw an IPE here. We ignore it because
+ // when the item is actually changed, a CollectionChanged event will be
+ // be dispatched. See handleCollectionChangeEvent().
+ }
+
+ if (failedItem !== undefined)
+ return failedItem;
+ else
+ return (pendingResponder) ? pendingResponder.item : setItemValue;
+ }
+
+ /**
+ * Returns an array with the same elements as this AsyncListView. The array is initialized
+ * by retrieving each item with <code>getItemAt()</code>, so pending items are substituted where actual
+ * values aren't available yet. The array will not be updated when the AsyncListView replaces
+ * the pending items with actual (or failed) values.
+ *
+ * @return an array with the same elements as this AsyncListView.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function toArray():Array
+ {
+ if (!list)
+ return [];
+
+ const a:Array = new Array(list.length);
+ for(var i:int = 0; i < a.length; i++)
+ a[i] = getItemAt(i);
+ return a;
+ }
+
+
+ /**
+ * Returns a string that contains the list's length and the number of pending item requests.
+ * It does not trigger pending requests.
+ *
+ * @return A brief description of the list.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ public function toString():String
+ {
+ var s:String = getQualifiedClassName(this);
+
+ if (list)
+ {
+ var nRequests:int = 0;
+ for each (var responder:ListItemResponder in pendingResponders)
+ {
+ if (responder)
+ nRequests += 1;
+ }
+ s += " length=" + length + ", " + nRequests + " pending requests";
+ }
+ else
+ s += " no list";
+
+ return s;
+ }
+
+}
+}
+
+import mx.rpc.IResponder;
+import mx.collections.AsyncListView;
+import mx.core.mx_internal;
+
+use namespace mx_internal; // for mx_internal functions pendingItemSucceeded,Failed()
+
+class ListItemResponder implements IResponder
+{
+ private var asyncListView:AsyncListView;
+ public var index:int = -1;
+ public var item:Object = null;
+
+ public function ListItemResponder(asyncListView:AsyncListView, index:int, item:Object)
+ {
+ super();
+ this.asyncListView = asyncListView;
+ this.index = index;
+ this.item = item;
+ }
+
+ public function result(info:Object):void
+ {
+ if (index != -1)
+ asyncListView.pendingRequestSucceeded(index, info);
+ }
+
+ public function fault(info:Object):void
+ {
+ if (index != -1)
+ asyncListView.pendingRequestFailed(index, info);
+ }
+}