Thursday, 18 March 2010

BetterDataGrid Improved

package
{
import flash.display.Sprite;
import flash.geom.Point;

import mx.collections.CursorBookmark;
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.mx_internal;
use namespace mx_internal;


/**
 *  A DataGrid subclass that has faster horizontal scrolling
 */
public class BetterDataGrid extends DataGrid
{
    public function BetterDataGrid()
    {
        super();
    }

    /**
     *  remember the number of columns in case it changes
     */
    private var lastNumberOfColumns:int;

    /**
     *  a flag as to whether we can use the optimized scrolling
     */
    private var canUseScrollH:Boolean;

    /**
     *  when the horizontal scrollbar is changed it will eventually set horizontalScrollPosition
     *  This value can be set programmatically as well.
     */
    override public function set horizontalScrollPosition(value:Number):void
    {
        // remember the setting of this flag.  We will tweak it in order to keep DataGrid from
        // doing its default horizontal scroll which essentially refreshes every renderer
        var lastItemsSizeChanged:Boolean = itemsSizeChanged;

        // remember the current number of visible columns.  This can get changed by DataGrid
        // as it recomputes the visible columns when horizontally scrolled.
        lastNumberOfColumns = visibleColumns.length;

        // reset the flag for whether we use our new technique
        canUseScrollH = false;

        // call the base class.  If we can use our technique we'll trip that flag
        super.horizontalScrollPosition = value;

        // if the flag got tripped run our new technique
        if (canUseScrollH)
        {
            scrollLeftOrRight();
            configureScrollBars();
        }

        // reset the flag
        itemsSizeChanged = lastItemsSizeChanged;

    }

    // remember the parameters to scrollHorizontally to be used in our new technique
    private var pos:int;
    private var deltaPos:int;
    private var scrollUp:Boolean;

    public function get attrColumnIndex():int {

        return _attrColumnIndex;
    }

    public function set attrColumnIndex(o:int):void {
        _attrColumnIndex = o;
    }

    protected var _attrColumnIndex:int = null;


    // override this method.  If it gets called that means we can use the new technique
    override protected function scrollHorizontally(pos:int, deltaPos:int, scrollUp:Boolean):void
    {
        // just remember the args for later;
        this.pos = pos;
        this.deltaPos = deltaPos;
        this.scrollUp = scrollUp;
        if (deltaPos < visibleColumns.length)
        {
            canUseScrollH = true;

            // need this to prevent DG from asking for a full refresh
            itemsSizeChanged = true;
        }
    }

    /**
     *  The new technique does roughly what we do vertically.  We shift the renderers on screen and in the
     *  listItems array and only make the new renderers.
     *  Because we can't get internal access to the header, we fully refresh it, but that's only one row
     *  of renderers.  There's significant gains to be made by not fully refreshing the every row of columns
     *
     *  Key thing to note here is that visibleColumns has been updated, but the renderer array has not
     *  That's why we don't do this in scrollHorizontally as the visibleColumns hasn't been updated yet
     *  But because of that, sometimes we have to measure old renderers, and sometimes we measure the columns
     */
    private function scrollLeftOrRight():void
    {
        // trace("scrollHorizontally " + pos);
        var i:int;
        var j:int;

        var numCols:int;
        var uid:String;

        var curX:Number;

        var rowCount:int = rowInfo.length;
        var columnCount:int = listItems[0].length;
        var cursorPos:CursorBookmark;

        var moveBlockDistance:Number = 0;

        var c:DataGridColumn;
        var item:IListItemRenderer;
        var itemSize:Point;
        var data:Object;

        var xx:Number;
        var yy:Number;

        if (scrollUp) // actually, rows move left
        {
            // determine how many columns we're discarding
            var discardCols:int = deltaPos;

            // measure how far we have to move by measuring the width of the columns we
            // are discarding

            moveBlockDistance = sumColumnWidths(discardCols, true);
            // trace("moveBlockDistance = " + moveBlockDistance);

            //  shift rows leftward and toss the ones going away
            for (i = 0; i < rowCount; i++)
            {
                numCols = listItems[i].length;

                 if(numCols == 0) //empty row
                   continue;

                // move the positions of the row, the item renderers for the row,
                // and the indicators for the row
                moveRowHorizontally(i, discardCols, -moveBlockDistance, numCols);
                // move the renderers within the array of rows
                shiftColumns(i, discardCols, numCols);
                truncateRowArray(i);
            }

            // generate replacement columns
            cursorPos = iterator.bookmark;

            var firstNewColumn:int = lastNumberOfColumns - deltaPos;
            curX = listItems[0][firstNewColumn - 1].x + listItems[0][firstNewColumn - 1].width;


            for (i = 0; i < rowCount; i++)
            {
                if(iterator == null || iterator.afterLast || !iteratorValid)
                   continue;
                data = iterator.current;
                iterator.moveNext();
                uid = itemToUID(data);

                xx = curX;
                yy = rowInfo[i].y;
                for (j = firstNewColumn; j < visibleColumns.length; j++)
                {
                    c = visibleColumns[j];
                    item = setupColumnItemRenderer(c, listContent, i, j, data, uid);
                    //if(!item) return;
                    itemSize = layoutColumnItemRenderer(c, item, xx, yy);
                    xx += itemSize.x;
                }
                // toss excess columns
                while (listItems[i].length > visibleColumns.length)
                {
                    addToFreeItemRenderers(listItems[i].pop());
                }
            }

            iterator.seek(cursorPos, 0);
        }
        else
        {
            numCols = listItems[0].length;

            if(deltaPos > visibleColumns.length)
             deltaPos = visibleColumns.length;

            moveBlockDistance = sumColumnWidths(deltaPos, false);

            // shift the renderers and slots in array
            for (i = 0; i < rowCount; i++)
            {
                numCols = listItems[i].length;
                 if(numCols == 0)
                    continue;

                moveRowHorizontally(i, 0, moveBlockDistance, numCols);
                // we add placeholders at the front for new renderers
                addColumnPlaceHolders(i, deltaPos);

            }

            cursorPos = iterator.bookmark;

            for (i = 0; i < rowCount; i++)
            {
                data = iterator.current;
                iterator.moveNext();
                uid = itemToUID(data);

                xx = 0;
                yy = rowInfo[i].y;
                for (j = 0; j < deltaPos; j++)
                {
                    c = visibleColumns[j];
                    item = setupColumnItemRenderer(c, listContent, i, j, data, uid);
                    itemSize = layoutColumnItemRenderer(c, item, xx, yy);
                    xx += itemSize.x;
                }
                // toss excess columns
                while (listItems[i].length > visibleColumns.length)
                {
                    addToFreeItemRenderers(listItems[i].pop());
                }
            }

            iterator.seek(cursorPos, 0);
        }

        // force update the header
        header.headerItemsChanged = true;
        header.visibleColumns = visibleColumns;
        header.invalidateDisplayList();
        header.validateNow();

        // draw column lines and backgrounds
        drawLinesAndColumnBackgrounds();
    }

    /* override protected function addToFreeItemRenderers(item:IListItemRenderer):void
    {
        if (item) super.addToFreeItemRenderers(item);
    } */

    // if moving left, add up old renderers
    // if moving right, add up new columns
    private function sumColumnWidths(num:int, left:Boolean):Number
    {
        var i:int;
        var value:Number = 0;
        if (left)
        {
            for (i = 0; i < num; i++)
            {
                value += listItems[0][i].width;
            }
        }
        else
            for (i = 0; i < num; i++)
            {
                value += visibleColumns[i].width;
            }

        return value;
    }

    // shift position of renderers on screen
    private function moveRowHorizontally(rowIndex:int, start:int, distance:Number, end:int):void
    {
        for (;start < end; start++)
        if(listItems[rowIndex][start]){
            listItems[rowIndex][start].x += distance;
        }
    }

    // shift renderer assignments in listItems array
    private function shiftColumns(rowIndex:int, shift:int, numCols:int):void
    {
        var item:IListItemRenderer;
        var uid:String = itemToUID(listItems[rowIndex][0].data);
        for (var i:int = 0; i < shift; i++)
        {
            item = listItems[rowIndex].shift();
            if(item){
                addToFreeItemRenderers(item);
            }
        }
        //rebuild the listContent.visibleData map entry
        listContent.visibleData[uid] = listItems[rowIndex][0];
    }

    // add places in front of row for new columns
    private function addColumnPlaceHolders(rowIndex:int, count:int):void
    {
        for (var i:int = 0; i < count; i++)
        {
            listItems[rowIndex].unshift(null);
        }
    }

    // remove excess columns
    private function truncateRowArray(rowIndex:int):void
    {
        while (listItems[rowIndex].length > visibleColumns.length)
        {
            var item:IListItemRenderer;
            {
                item = listItems[rowIndex].pop();
                addToFreeItemRenderers(item);
            }
        }
    }

    override protected function drawVerticalLine(s:Sprite, colIndex:int, color:uint, x:Number):void
    {
        if(!attrColumnIndex){
            super.drawVerticalLine(s:Sprite, colIndex:int, color:uint, x:Number);
            return;
        }
        if (colIndex == attrColumnIndex-pos)
        {
            var colour:uint = 0xFF0000;
            super.drawVerticalLine(s,colIndex,colour,x);
            return;
        }
        super.drawVerticalLine(s,colIndex,color,x);
    }

 }
}

Posted via email from fasanya's posterous

Thursday, 11 March 2010

Mockito

Mockito is a framework to help you create stubs and mocks quickly if you do Unit Testing.

Check the link out and the tutorial. 

http://bitbucket.org/loomis/mockito-flex/wiki/Home

Tutorial for version 1.0

This tutorial is unit testing framework agnostic. For examples on how to use it with specific unit testing frameworks please check the integrations section.

Let's

Before you will be able to create your mock objects you need to tell Mockito to prepare for it. You do it by calling:

1 
mockito
.prepareClasses([...]) ;

and providing all the classes to mock in given test case. Since the bytecode generation is an asynchronous process it is recommended to use one of the integration test cases as they address this issue. You may find more answers inAsmock documentation.

After preparing classes you can create mock objects by invoking:

1 
var dependency:Dependency = Dependency(mockito.mock(Dependency)); 

Then setup the System Under Test (SUT)

1 
var sut:Sut = new Sut(dependency); 

And execute tested code.

1 
sut.testedFunction(10); 

Given the testedFunction looks like this:

1 2 3 4 
function testedFunction(input:int):void  {  dependencyResult = dependency.someOperation(input);  } 

Notice that there is no 'unexpected call' exception.

Instead you are free to choose what you want to verify:

1 
verify().that(dependency.someOperation(input)); 

The full test would look like this:

...  mockito.prepareClasses([Dependency]);  ...  var dependency:Dependency = Dependency(mockito.mock(Dependency));  // given  var sut:Sut = new Sut(dependency);  // when  sut.testedFunction(10);  // then  mockito.verify().that(dependency.someOperation(10)); 

As you can see, verification happens where assertions go. Not before the tested code. Important note is that verify() is equivalent to verify(times(1)).

If you need to stub dependency, you define it upfront.

1 
mockito.given(dependency.someOperation(10)).willReturn(1); 

When used in test it would look like this:

...  mockito.prepareClasses([Dependency]);  ...  var dependency:Dependency = Dependency(mockito.mock(Dependency));  // given  var sut:Sut = new Sut(dependency);  mockito.given(dependency.someOperation(10)).willReturn(1);  // when  sut.testedFunction(10);  // then  assertEquals(1, sut.dependencyResult); 

It may be useful to verify or define stubbing with various arguments at a time or have ability of matching the specific cases. For that purpose Mockito provides Matchers. Below example verifies any arguments passed to the function:

1 
mockito.verify().that(dependency.someOperation(mockito.any())); 

Similar you can do with stubbing:

1 
mockito.given(dependency.someOperation(any())).willReturn(1); 

As you can see you can either use explicit values or matchers when defining stubs or verifying. But you cannot mix it within single stub definition or verification. So for instance:

1 
mockito.verify().that(progress.update(10, mockito.any())) 

is invalid. Mockito will not be able to figure out which argument type to match against the any() matcher. You may want to verify multiple executions of a method at time. It's easy. Just tell how to verify:

1 
mockito.verify(mockito.times(3)).that(dependency.someOperation(mockito.any())); 

Sometimes you may want to make sure method has not been called. Do it by verifying:

Posted via email from fasanya's posterous

Ever wondered how to see the entire stack trace how you got to a particular method? (without trawling through eclipse and tons fo breakpoints)

trace(new Error().getStackTrace())

Handy …. J

The contents of this message and any attachments to it are confidential and may be legally privileged. If you have received this message in error you should delete it from your system immediately and advise the sender. dunnhumby may monitor and record all emails. The views expressed in this email are those of the sender and not those of dunnhumby.

Posted via email from fasanya's posterous

Nice Css list for flex.

http://www.loscavio.com/downloads/blog/flex3_css_list/flex3_css_list.htm

The contents of this message and any attachments to it are confidential and may be legally privileged. If you have received this message in error you should delete it from your system immediately and advise the sender. dunnhumby may monitor and record all emails. The views expressed in this email are those of the sender and not those of dunnhumby.

Posted via email from fasanya's posterous

Friday, 26 February 2010

My Eclipse Installations Reference

The contents of this message and any attachments to it are confidential and may be legally privileged. If you have received this message in error you should delete it from your system immediately and advise the sender. dunnhumby may monitor and record all emails. The views expressed in this email are those of the sender and not those of dunnhumby.

Posted via email from fasanya's posterous

Monday, 15 February 2010

Doc for flex and flash.


Yep you can get documentation on as3 from within eclipse but not offline and now its a whole lot easier to set up, you can even bookmark pages, and it's done in air check it out and show your support:
http://www.airdoc.be/

The eclipse plugin and other downloads:
http://www.airdoc.be/download#eclipse