blob: b4d669d809a319097271d1755cb29cfbdbe01219 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.EventDispatcher;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.utils.UIDUtil;
import mx.core.mx_internal;
use namespace mx_internal;
/**
* @private
* This class provides a heirarchical view (a tree-like) view of a standard collection.
* The collection that this Cursor walks across need not be heirarchical but may be flat.
* This class delegates to the IHierarchicalData for information regarding the tree
* structure of the data it walks across.
* This class assumes that all the nodes are open and then traverse through them.
*
* Only methods moveNext() and movePrevious() are implemented.
*
* @see HierarchicalCollectionView
*/
public class LeafNodeCursor extends EventDispatcher
implements IHierarchicalCollectionViewCursor
{
// include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function LeafNodeCursor(
collection:HierarchicalCollectionView,
model:ICollectionView,
hierarchicalData:IHierarchicalData)
{
super();
//fields
this.collection = collection;
this.hierarchicalData = hierarchicalData;
this.model = model;
//init
modelCursor = model.createCursor();
//check to see if the model has more than one top level items
if (model.length > 1)
more = true;
else
more = false;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var hierarchicalData:IHierarchicalData;
/**
* @private
* Its effective offset into the "array".
*/
private var currentIndex:int = 0;
/**
* @private
* The current index into the childNodes array
*/
private var currentChildIndex:int = 0;
/**
* @private
* The depth of the current node.
*/
private var _currentDepth:int = 1;
/**
* @private
* The current set of childNodes we are walking.
*/
private var childNodes:Object = [];
/**
* @private
* The current set of parentNodes that we have walked from
*/
private var parentNodes:Array = [];
/**
* @private
* A stack of the currentChildIndex in all parents of the currentNode.
*/
private var childIndexStack:Array = [];
/**
* @private
* The collection that stores the user data
*/
private var model:ICollectionView;
/**
* @private
* The collection wrapper of the model
*/
private var collection:HierarchicalCollectionView;
/**
* @private
* Flag indicating model has more data
*/
private var more:Boolean;
/**
* @private
* Cursor from the model
*/
private var modelCursor:IViewCursor;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// index
//----------------------------------
/**
* @private
*/
public function get index():int
{
return currentIndex;
}
//----------------------------------
// bookmark
//----------------------------------
/**
* @private
*/
public function get bookmark():CursorBookmark
{
return null;
}
//----------------------------------
// current
//----------------------------------
/**
* @private
*/
public function get current():Object
{
try
{
if (childIndexStack.length == 0)
{
if (hierarchicalData.canHaveChildren(modelCursor.current) ||
modelCursor.current is SummaryObject)
{
moveNext();
}
else
{
return modelCursor.current;
}
}
return childNodes[currentChildIndex];
}
catch (e:RangeError)
{
}
return null;
}
//---------------------------------
// currentDepth
//---------------------------------
/**
* @private
*/
public function get currentDepth():int
{
return _currentDepth;
}
//----------------------------------
// beforeFirst
//----------------------------------
/**
* @private
*/
public function get beforeFirst():Boolean
{
return currentIndex < 0 && current == null;
}
//----------------------------------
// afterLast
//----------------------------------
/**
* @private
*/
public function get afterLast():Boolean
{
return currentIndex >= collection.length && current == null;
}
//----------------------------------
// view
//----------------------------------
/**
* @private
*/
public function get view():ICollectionView
{
return model;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
public function findAny(values:Object):Boolean
{
return false;
}
/**
* @private
*/
public function findFirst(values:Object):Boolean
{
return false;
}
/**
* @private
*/
public function findLast(values:Object):Boolean
{
return false;
}
/**
* @private
* Move one node forward from current.
* This may include moving up or down one or more levels.
*/
public function moveNext():Boolean
{
var currentNode:Object = childIndexStack.length == 0 ? modelCursor.current : current;
//if there is no currentNode then we cant move forward and must be off the ends
if (currentNode == null)
return false;
var uid:String = UIDUtil.getUID(currentNode);
collection.addParentMapping(uid, parentNodes[parentNodes.length - 1], false);
var flag:Boolean = true;
// If current node is a branch and is open, the first child is our next item so return it
if (hierarchicalData.canHaveChildren(currentNode) &&
hierarchicalData.hasChildren(currentNode))
{
var previousChildNodes:Object = childNodes;
childNodes = collection.getChildren(currentNode);
if (childNodes.length > 0)
{
childIndexStack.push(currentChildIndex);
parentNodes.push(currentNode);
currentChildIndex = 0;
currentNode = childNodes[0];
currentIndex++;
_currentDepth++;
// check for children and currentNode to be SummaryObject and find the next item if required.
if (hierarchicalData.canHaveChildren(currentNode) ||
currentNode is SummaryObject)
{
flag = moveNext();
}
return flag;
}
else
{
childNodes = previousChildNodes;
}
}
// Otherwise until we find the next child (could be on any level)
while (true)
{
// If we hit the end of this list, pop up a level.
if (childNodes != null &&
childNodes.length > 0 &&
currentChildIndex >= Math.max(childNodes.length - 1, 0))
{
//check for the end of the tree here.
if (childIndexStack.length < 1 && !more)
{
currentNode = null;
currentIndex++;
_currentDepth = 1;
return false;
}
else
{
//pop up to parent
currentNode = parentNodes.pop();
//get parents siblings
var grandParent:Object = parentNodes[parentNodes.length-1];
//we could probably assume that a non-null grandparent has descendants
//but the analogy only goes so far...
if (grandParent != null &&
hierarchicalData.canHaveChildren(grandParent) &&
hierarchicalData.hasChildren(grandParent))
{
childNodes = collection.getChildren(grandParent);
}
else
{
childNodes = [];
}
//get new current nodes index
currentChildIndex = childIndexStack.pop();
//pop the level up one
_currentDepth--;
}
}
else
{
//if no childnodes then we're probably at the top level
if (childIndexStack.length == 0)
{
//check for more top level siblings
//and if we're here the depth should be 1
_currentDepth = 1;
more = modelCursor.moveNext();
if (more)
{
currentNode = modelCursor.current;
break;
}
else
{
//at the end of the tree
_currentDepth = 1;
currentIndex++; //this should push us to afterLast
currentNode = null;
return false;
}
}
else
{
//get the next child node
try
{
currentNode = childNodes[++currentChildIndex];
break;
}
catch (e:RangeError)
{
//lets try to recover
return false;
}
}
}
}
currentIndex++;
// check for children and currentNode to be SummaryObject and find the next item if required.
if (hierarchicalData.canHaveChildren(currentNode) ||
currentNode is SummaryObject)
{
flag = moveNext();
}
return flag;
}
/**
* @private
* Performs a backward tree walk.
*/
public function movePrevious():Boolean
{
var currentNode:Object = current;
// If there are no items, there's no current node, so return false.
if (currentNode == null)
return false;
var flag:Boolean = true;
//if not at top level
if (parentNodes && parentNodes.length > 0)
{
if (currentChildIndex == 0)
{
//at the first node in this branch so move to parent
currentNode = parentNodes.pop();
currentChildIndex = childIndexStack.pop();
var grandParent:Object = parentNodes[parentNodes.length-1];
//we could probably assume that a non-null grandparent has descendants
//but the analogy only goes so far...
if (grandParent != null &&
hierarchicalData.canHaveChildren(grandParent) &&
hierarchicalData.hasChildren(grandParent))
{
childNodes = collection.getChildren(grandParent);
}
else
{
//null is valid, but error prone so we'll make it empty
childNodes = [];
}
_currentDepth--;
currentIndex--;
// check for children and currentNode to be SummaryObject and find the previous item if required.
if (!beforeFirst && (hierarchicalData.canHaveChildren(currentNode) ||
currentNode is SummaryObject))
{
flag = movePrevious();
}
return flag;
}
else
{
// get previous child sibling
try
{
currentNode = childNodes[--currentChildIndex];
}
catch(e:RangeError)
{
//lets try to recover
return false;
}
}
}
//handle top level siblings
else
{
more = modelCursor.movePrevious();
if (more)
{
//move back one node and then loop through children
currentNode = modelCursor.current;
}
//if past the begining of the tree return false
else
{
//currentIndex--; //should be before first
currentIndex = -1;
currentNode = null;
return false;
}
}
while (true)
{
// If there are children, walk backwards on the children
// and consider youself after your children.
if (hierarchicalData.canHaveChildren(currentNode) &&
hierarchicalData.hasChildren(currentNode))
{
var previousChildNodes:Object = childNodes;
childNodes = collection.getChildren(currentNode);
if (childNodes.length > 0)
{
childIndexStack.push(currentChildIndex);
parentNodes.push(currentNode);
currentChildIndex = childNodes.length - 1;
currentNode = childNodes[currentChildIndex];
_currentDepth++;
}
else
{
childNodes = previousChildNodes;
break;
}
}
else
{
//No more open branches so we'll bail
break;
}
}
currentIndex--;
// check for children and currentNode to be SummaryObject and find the previous item if required.
if (hierarchicalData.canHaveChildren(currentNode) ||
currentNode is SummaryObject)
{
flag = movePrevious();
}
return flag;
}
/**
* @private
*/
public function seek(bookmark:CursorBookmark, offset:int=0,
prefetch:int = 0):void
{
//No impl
}
/**
* @private
*/
public function insert(item:Object):void
{
//No impl
}
/**
* @private
*/
public function remove():Object
{
return null;
}
}
}