teotigraphix.blog.show()

Flex 2 :: Tree :: Walking the Tree method

Hi,

I thought I would post this method to show some of you that the dataDescriptor rocks. One thing you will notice in the method below is there is an if() check on the model at every turn. With experience comes wisdom and I have found this is essential in creating methods that test the sands of time.

Also note, the method is 'actually' two methods. The first half of the method will back step on the item's parent. The nice thing about this is, if you do pass startAtParent as true, the first part of the method only gets executed once in the recursion algorithm. It's main purpose is to get the item's parent and siblings, loop through them and then branch down the chain.

This is handy if you want to know the item's sibling branches. If you do not need the sibling branches, pass startAtParent as false and you bypass the first part of the method and go straight to the item.

If the item is a leaf, it will just trace the leaf item. If the item is a branch, it will then recurse through the whole composite structure of the item passed.

I hope this helps people, and you can discover some of the fascinating aspects about recursion and dataDescriptors. OOP is all about abstraction and decoupling through dataDescriptors. Adobe has found a wonderful balance in all the composite controls they have made thus far.

Look for the comments where you could break into your own method branch.

/**
 * This method will traverse a Tree's model independent of it's
 * type.
 *
 * <p>Note :: This method may look long and arduous but, rest assured
 * it has all the checks to perform like a champ. Also, you 'could'
 * refactor part of this method but, for the sake of explanation, I
 * kept it all in one place.</p>
 *
 * <p>Remember, I had coupled the model to this method by tracing
 * @label, obviously you do not need to do this. The intention of
 * this example is to show you that the dataDescriptor seperates
 * the models type and is awesome. It enables you to create a tight
 * method like this without type checks on the model.</p>
 *
 * @param tree The Tree instance that will be examined by the method.
 * @param item An item found in the dataProvider of the Tree passed in.
 * @param startAtParent A boolean that determines if the method upon
 * initialization will back up one leve3l to the item passed in and
 * start it's recursion at the item's parent node.
 */

public function walkTree(tree:Tree, item:Object, startAtParent:Boolean = false):void
{
    // get the Tree's data descriptor
    var descriptor:ITreeDataDescriptor = tree.dataDescriptor;
    var cursor:IViewCursor;
   
    var parentItem:Object;
    var childItem:Object;
    var childItems:Object;
   
    // if the item is null, stop
    if (item == null)
        return;
       
    // do we back up one level to the item's parent
    if (startAtParent)
    {
        // get the parent
        parentItem = tree.getParentItem(item);
        // is the parent real
        if (parentItem)
        {
            trace("|-- Parent Node ", parentItem[tree.labelField]);
            // if the parent is a branch
            if (descriptor.isBranch(parentItem))
            {
                // if the branch has children to run through
                if (descriptor.hasChildren(parentItem))
                {
                    // get the children of the branch
                    // this part of the algorithm contains the item
                    // passed
                    childItems = descriptor.getChildren(parentItem);
                }
            }
            // if the branch has valid child items
            if (childItems)
            {
                // create our back step cursor
                cursor = childItems.createCursor();
                // loop through the items parent's children (item)
                while (!cursor.afterLast)
                {
                    // get the current child item
                    childItem = cursor.current;

                    var label:String = childItem[tree.labelField];
                    var branch:Boolean = descriptor.isBranch(childItem);
                   
                    // good place for a custom method()
                    trace("Sibling Nodes :: ", label, "Is Branch :: ", branch);
                   
                    // if the child item is a branch
                    if (descriptor.isBranch(childItem))
                        // traverse the childs branch all the way down
                        // before returning
                        walkTree(tree, childItem);
                    // do it again!
                    cursor.moveNext();
                }
            }
        }
    }
    else // we don't want the parent OR this is the second iteration
    {
        // if we are a branch
        if (descriptor.isBranch(item))
        {
            // if the branch has children to run through
            if (descriptor.hasChildren(item))
            {
                // get the children of the branch
                childItems = descriptor.getChildren(item);
            }
           
            // if the child items exist
            if (childItems)
            {
                // create our cursor pointer
                cursor = childItems.createCursor();
                // loop through all of the children
                // if one of these children are a branch we will recurse
                while (!cursor.afterLast)
                {
                    // get the current child item
                    childItem = cursor.current;

                    var label:String =  childItem[tree.labelField];
                    var branch:Boolean = descriptor.isBranch(childItem);
                   
                    // good place for a custom method()
                    trace("-- Sub Node :: ", label, "Is Branch :: ", branch);

                    // if the child item is a branch
                    if (descriptor.isBranch(childItem))
                        // traverse the childs branch all the way down
                        // before returning
                        walkTree(tree, childItem);
                    // check the next child
                    cursor.moveNext();
                }
            }
        }
    }
}
 

Peace, Mike

19 Responses to “Flex 2 :: Tree :: Walking the Tree method”

  1. Campbell Says:

    Kewl, nice example Mike. Cheers for sharing and helping people out. Im getting alot of questions about the tree, means thereshould be alot of demand for your cool components coming out soon :)

  2. teoti.graphix Says:

    > Cheers for sharing and helping people out.

    The irony of this statement is in my Flash days, that is all I did. Now it’s time to put food on the table for the family. ;-)

    The bottom line is, the more established I get, the more I can help. You probably know what I am talking about.

    My site is set up as a community site, although it’s in it’s infant stage, it will have plenty of ‘free’ stuff. Just let me get ahead of myself.

    Peace, Mike

  3. Josh Tynjala Says:

    Data descriptors are pretty cool. I just started working with them because I’m building a component that needs to be compatible with the Tree component (in terms of acceptable data sources).

  4. teoti.graphix Says:

    Hi Josh,

    I can’t remember but were you the one that was playing with the transformer? I have you narrowed down to two people in my head that I can think of. :)

    BTW, I like your MXNA FLex app, that is pretty kewl, what are you doing for the layout, I bet you could get it to resize smoother. ;-)

    DataDescriptors are the most under rated elements in the Flex 2 model system right now. I have some articles I have written on them. The CheckBoxTreeFX uses them extensivly and I can see a bunch of my custom components that need models to pipe into them.

    One of the greatest things I see comming from dataDescriptors is internationalization.

    Anyway nice to see your popping in.

    Peace, Mike

  5. Campbell Says:

    Mike, “One of the greatest things I see comming from dataDescriptors is internationalization.” thats where I would look at namespacing the xml data ;)

    Dont worry the first chance I get (when a client needs it), I know exactly whos door to knock on. I wanna play and make some cool combinations. Do you do contracting too?

  6. teoti.graphix Says:

    > Do you do contracting too?

    :) Depends, is there baby sitting involved. :) haha.

    Right now I am making a really complex dnd framewrok of components so probably not at the moment. Who knows down the road, I used to.

    > dataDescriptors is internationalization.

    Really with this comment I just meant being able to change field names on data sets at runtime. :)

    Peace, Mike

  7. Jabbypanda Says:

    This walking Tree method kicks some ass, definitely something I could not achieve by myself in a short period of time ahead of me, because for me concept of the data descriptor is still covered with a fog :)

    Mike, I have a question to you, can I pass a custom function name (preferably with its own list of parameters) as a single input parameter to walkTree method?

    In that case I could reuse walkTree method between multiple different function such as cutTreeNodes, pasteTreeNodes, disableTreeNodes, etc….

  8. teoti.graphix Says:

    Hey,

    Yeah, sure!

    In the places where you see ‘// good place for a custom method()’ you could pass a function ref in the original method call.

    As far as arguments, maybe pass an object as a forth parameter, then you could have variable argument lengths.
    Or you could use the aruments object for the call itself. I have thought about what you asked but, never needed to implement it in any of my projects so I never figured it out.

    :)

    Peace, Mike

  9. JabbyPanda Says:

    Thanks Mike for an answer.

    I was thinking about something similar solution that you had just suggested, but wanted to have more clarification on this subject.

    I found this example in Livedocs that relies on arguments.callee
    http://livedocs.macromedia.com/labs/as3preview/langref/arguments.html

    Looks like a pretty good approach to call the custom function (firstFunction() in example) within a body of walkTree method (secondFunction() in example)

    Is this something like
    package {
    import flash.display.Sprite;

    public class ArgumentsExample extends Sprite {
    private var count:int = 1;

    public function ArgumentsExample() {
    firstFunction(true);
    }

    public function firstFunction(callSecond:Boolean) {
    trace(count + “: firstFunction”);
    if(callSecond) {
    secondFunction(package {
    import flash.display.Sprite;

    public class ArgumentsExample extends Sprite {
    private var count:int = 1;

    public function ArgumentsExample() {
    firstFunction(true);
    }

    public function firstFunction(callSecond:Boolean) {
    trace(count + “: firstFunction”);
    if(callSecond) {
    secondFunction(arguments.callee);
    }
    else {
    trace(”CALLS STOPPED”);
    }
    }

    public function secondFunction(caller:Function) {
    trace(count + “: secondFunction\n”);
    count++;
    caller(false);
    }
    }
    });
    }
    else {
    trace(”CALLS STOPPED”);
    }
    }

    public function secondFunction(caller:Function) {
    trace(count + “: secondFunction\n”);
    count++;
    caller(false);
    }
    }
    }

  10. Michael Says:

    Mike,

    Does this work if the tree uses a labelfunction or a custom renderer? I am using a labelfunction and when this method tries to execute this statement:

    var label:String = childItem[tree.labelField];

    label is set to “”.

    When I look at tree.labelField in the debugger I see “label”. I am using XML to populate the tree and childItem is XML when I look at it. Does it sound like I am doing something wrong or do you need more details?

    Here is my trace after going through a few nodes:
    – Sub Node :: Is Branch :: true
    – Sub Node :: Is Branch :: true

    Thank you,
    Michael

  11. Barry Evans Says:

    Can i ask a quick (and probably simply answered question!)?
    Im finding it tough to get my head round the ITreeDataDescriptor class in Flex 2.

    I want to walk through a tree in my application, and your function is definatley going to help.
    The thing is, i want to walk through the whole tree FROM THE FIRST NODE!

    The second parameter your function takes is a node of the tree (i am guessing), this has been a problem i have been trying to figure out for the past 4-5 hours! I cant figure out how to get access to the trees first XML node.

    How do i get the function to walk through a tree simply by passing the tree as a parameter?

    I hope you can help, i am stuck in my tracks with some very important development until i can get this done… and the deadline approaches… FAST >

    ;

    private function initTree() : void{
    folderCollection = new XMLListCollection(folderList);
    checkTree.dataProvider = folderCollection;
    }

  12. Barry Evans Says:

    Can i ask a quick (and probably simply answered question!)?
    Im finding it tough to get my head round the ITreeDataDescriptor class in Flex 2.

    I want to walk through a tree in my application, and your function is definatley going to help.
    The thing is, i want to walk through the whole tree FROM THE FIRST NODE!

    The second parameter your function takes is a node of the tree (i am guessing), this has been a problem i have been trying to figure out for the past 4-5 hours! I cant figure out how to get access to the trees first XML node.

    How do i get the function to walk through a tree simply by passing the tree as a parameter?

    I hope you can help, i am stuck in my tracks with some very important development until i can get this done… and the deadline approaches… FAST

    Thanks in advance.

    Barry

    p.s.
    Im not sure this is of great importance or help, but i will add my XML and how i assign it to my tree’s dataprovider:

    ;

    private function initTree() : void{
    folderCollection = new XMLListCollection(folderList);
    checkTree.dataProvider = folderCollection;
    }
    ]]>

  13. teoti.graphix Says:

    Hi Barry,

    walkTree(checkTree, checkTree.dataProvider[0]);

    should do it if I remember correctly.

    Peace, Mike

    PS, depends i think if you are using showRoot, and what your XML looks like. If you don’t have just ‘one’ root, you will need to create a for() loop that loops through the base of your tree and calls walkTree().

  14. Barry Evans Says:

    Thanks Mike your suggestion worked 100%, your a *

    Your function is a great little piece of code… its been a while since i had to write a recursive function, it would probably have taken me about to a week to get through that one on my own!

    Keep up the good work, and thanks again for your help

  15. Maarten Says:

    Dear People,

    I’m “migrating” from Flash MX to Flex, and although I was quite dexterious in Flash, I feel completely lost. I’m just wondering: is it just me, or _has_ everything become MUCH more complicated? I mean, why are there no simple methods anymore like nextChild and so fourth. It is indeed a great piece of work you, but why should it be necassary?

    I’m curious to hear your (plurar) opinions about it.

    Regards!

  16. teoti.graphix Says:

    Hi,

    One Acronym, OOP. Object Oriented Programming uses interfaces.

    I started in Flash 5, so yeah I hear what you are saying. But the more complicated things get( the power of Flex and as3) the tighter you need your logic and structure to be.

    This just means there is a greater language to learn with more words. But, the more vocabulary you have to work with in any language, lets you express yourself faster and more eloquently.

    Peace, Mike

  17. Maarten Says:

    teoti.graphix, thanks!

    Yep, true, I see that. But for a OOP-noop like me, the gap is quite large. Learning the OOP is indeed very interresting, but I`d like to do so starting with Flash instead of with -say- C++, which I’ll never need in the rest of my life. ;-) Can anybody recommand any literature that does just that: teach OOP in a AS3 - setting ?

  18. Joshuajh Says:

    Many thanks!

    But, I’m barely know how to use this method if I want to traverse from the tree root.
    can someone provide a example for me?

    this is my code:

    public function showtree (): void {
    walkTree (myTree, >);
    }

  19. Mohana Says:

    Hi Mike,

    Your tip to browse the tree from the root worked for me. It was a great help.

    walkTree(checkTree, checkTree.dataProvider[0]);

    Joshuajh , it should work for you too, if you are using a XMLLListCollection as dataProvider for your tree.

    Regards
    Mohan Mamidala

Leave a Reply