package flare.vis.data { import flare.vis.events.DataEvent; /** * Data structure for managing a collection of visual data objects in a * tree (hierarchy) structure. This class extends the functionality of * the Data class to model a hierarchy. The class can be used as an * alternative to the Data class when the data forms a strict tree, or to * model a spanning tree over a general graph. */ public class Tree extends Data { /** * Sets the root node of this tree. This property can only be set * when the tree does not yet have a root node. */ public override function set root(n:NodeSprite):void { if (_root == null) { super.addNode(n); _root = n; _root.parentIndex = -1; addDescendants(_root); } else { throw new ArgumentError("Can't set root unless the tree is empty." + "If you want to set an entirely new root, call clear() first."); } } /** This property simply points back to this object. */ public override function get tree():Tree { return this; } public override function set tree(t:Tree):void { /* do nothing */ } // -- Methods --------------------------------------------------------- /** * Creates and returns a new root NodeSprite for the tree. If the * tree already has a root, this method throws an error. * @return the newly added root */ public function addRoot():NodeSprite { if (_root != null) throw new Error( "addRoot can only be called on an empty tree!"); return (_root = super.addNode()); } /** * Add a node to the tree. If the child node argument is null, * a new NodeSprite will be created. A new EdgeSprite connecting * the nodes will be created. Returns the new child node. * @param p the parent node * @param c the child node. If null, a new child node is created. * @return the new child node */ public function addChild(p:NodeSprite, c:NodeSprite=null):NodeSprite { if (!_nodes.contains(p)) { throw new ArgumentError("Parent node must be in the tree!"); } c = super.addNode(c); var e:EdgeSprite = newEdge(p, c, directedEdges, null); c.parentIndex = p.addChildEdge(e); c.parentEdge = e; super.addEdge(e); return c; } /** * Adds the given edge as a child edge between a node already * in the tree and another node not yet in the tree. * @param e the edge to add to the tree * @return the newly added edge */ public function addChildEdge(e:EdgeSprite):EdgeSprite { var n1:NodeSprite = e.source, b1:Boolean = _nodes.contains(n1); var n2:NodeSprite = e.target, b2:Boolean = _nodes.contains(n2); if (b1 && b2) { throw new ArgumentError("One node must not be in the tree"); } if (!(b1 || b2)) throw new ArgumentError("One node must already be in the tree"); var p:NodeSprite = b1 ? n1 : n2; var c:NodeSprite = b1 ? n2 : n1; c.parentEdge = e; c.parentIndex = p.addChildEdge(e); super.addNode(c); return super.addEdge(e); } /** * Registers all tree descendants with this Tree instance. * @param n a sub-tree root that has already been added to this Tree */ protected function addDescendants(n:NodeSprite):void { for (var i:int=0; i * WARNING: this method causes connecting edges to be * reconfigured. If this tree is a spanning tree, this can cause havoc. *

* @param n the node to swap with its parent until it becomes the root */ public function swapToRoot(n:NodeSprite):void { while (n.parent != null) { swapWithParent(n); } } /** * Swaps the given node with its parent node. *

* WARNING: this method causes connecting edges to be * reconfigured. If this tree is a spanning tree, this can cause havoc. *

* @param n the node to swap with its parent */ public function swapWithParent(n:NodeSprite):void { var p:NodeSprite = n.parentNode, gp:NodeSprite; var e:EdgeSprite, ge:EdgeSprite, idx:int; if (p==null) return; gp = p.parentNode; ge = p.parentEdge; idx = p.parentIndex; // swap parent edge e = n.parentEdge; p.removeChild(n); p.parentEdge = e; p.parentIndex = n.addChildEdge(e); // connect to grandparents if (gp==null) { n.parentIndex = -1; n.parentEdge = null; } else { if (ge.source == gp) { ge.target = n; } else { ge.source = n; } n.parentIndex = idx; n.parentEdge = ge; } } } // end of class Tree }