A more controlled way to travel a collection: IViewCursor

I recently had to convert a very processor intensive command I had been working on into a psudo-threaded command (using my Command Framework).  It had been a while since I did and I had forgotten a few of the tricks that make it easy to work with so I’m putting them down so I don’t forget.

Generally when I create a threaded command I’m working with a large pile of data.  The threaded command works by calling handleRun() every frame (or every two or three frames, depending on how high a priority the command is).  You can’t just rip through all of the data elements; that kind of defeats the purpose of breaking it up into a psudo-thread.  Additionally the command I’ve built will attempt to optimize the number of elements you process in each run if you give it a little hand.  You could just keep an index of where you’re at in the array and increment that. That’s small and tidy, doesn’t require an (often bulky) ICollectionView and I really recommend doing that if you can. But sometimes you need to do a little more while you’re running through your data.

To do that you could use a IViewCursor.  The collection view cursor is a very handy tool that I forget to use more often than I should.  I certainly don’t utilize its full potential in this example but it does do a good job of keeping track of things for me.

Let’s say you start off with an XMLList pulled from some XML.  We need to work instead on an XMLListCollection so make sure to create one of those.  (Likewise you would need to use an ArrayCollection rather than an Array, remember we must work with the ICollectionView.)  Then just create a view cursor from that collection with the createCursor() method.

var xmlList:XMLList = myXml.node..items;
var collection:XMLListCollection = new XMLListCollection(xmlList);
var cursor:IViewCursor = collection.createCursor();
 

Now instead of getting an element from your collection like this: element = collection[i]; you can instead grab it from your cursor like this: element = cursor.current; and use the methods on the cursor to move through (and modify) the collection.

Here is a very brief example of how I use it in a ThreadedCommand.

package com.pbking.application.commands
{
  import com.ender.threads.Thread;
  import com.pbking.app.command.ThreadedCommand;
  import mx.collections.IViewCursor;
  import mx.collections.XMLListCollection;

  public class ParseClientData extends ThreadedCommand
  {
    private var data:XML;
    private var seatsToProcess:XMLListCollection;
    private var cursor:IViewCursor;

    public function ParseClientData(data:XML)
    {
      super();
     
      this.data = data;
     
      //I’m not doing anything else while this is going on
      //so I’m setting it to URGENT PRIORITY so that it
      //runs every frame.
      this.priority = Thread.URGENT_PRIORITY;

      //this will calculate the optimum number of iterations
      //that should be made in a single run (starting with 2 per run)
      this.autoOptimizeIterations = true;
      this.iterationsPerRun = 2;
    }
   
    override protected function handleExecution():void
    {
      //handle execution is called once before any runs
      //create my collection from my data and a cursor from that collection
      seatsToProcess = new XMLListCollection(data.clientDataXML..seat);
      cursor = seatsToProcess.createCursor();
     
      //the first run will start in the super handler
      super.handleExecution();
    }
   
    override protected function handleRun():void
    {
      var infoX:XML;

      //checking to see if I have made as many passes as I should (iterationCount vs iterationsPerRun
      //or if I have moved past the end of the collection (cursor.afterLast)     
      while(iterationCount < iterationsPerRun && !cursor.afterLast)
      {
        //pull the current node from my cursor
        //I’ll move the cursor on at the end of this while loop
        infoX = cursor.current as XML;
       
        //do all of my magic processing on the infoX object.
        //processing each of these takes a while; that’s why it’s
        //in a psudo-thread.
       
        //the benefit of using a cursor here instead of a recorded index
        //is that now I can add/subtract things from my collection
        //(either here or elsewhere) and the code can still handle that

        //this next value helps the optimizer (in the parent ThreadedCommand class)
        //know how many elements were processed and will automatically
        //adjust the iterationsPerRun based on how long the run took
        //and the desired amount of time per run
        iterationCount++;
       
        // This moves the cursor to the next element.
        // cursor.current has now advanced to the next element
        // and if we’ve gone past the end then cursor.afterLast is true
        cursor.moveNext();
      }

      if(cursor.afterLast)
      {
        //all done; wrap up the command
        onComplete();  
      }
      else
      {
        //yeield the command. We’ll come back and finish up later
        //with another call to run()
        yield();
      }
    }
   
  }
}
 

Filed under flex · Tagged with ,

Comments

2 Responses to “A more controlled way to travel a collection: IViewCursor”
  1. Jake Hawkes says:

    Hey Jason, your commands look pretty useful. A lot of the time when people start creating frameworks and using design-patterns they’re really just over-architecting and making things more complicated, but I can see good uses for pretty much everything I’ve read on your blog lately. Keep up the good work! (and keep blogging about it so I can benefit from it too)

  2. Jason Crist says:

    Thanks a bunch for that Jake! I’ll try to keep it up.

    I hope it’s obvious that I appreciate and use design patterns and best-practices but wish people were more aware of the “thing” they are building instead of just building the same thing over and over . . . that just happens to do different stuff.

Yea but . . .

Tell me what you're thinking.