<?php
/**
 * FIT Fixture: Row
 * 
 * $Id$
 * 
 * @author gERD Schaufelberger <gerd@php-tools.net>
 * @package FIT
 * @subpackage Fixture
 * @license LGPL http://www.gnu.org/copyleft/lesser.html
 */

/**
 * FIT Fixture: Row
 * 
 * A RowFixture compares rows in the test data to objects in the 
 * system under test. Methods are invoked on the objects and 
 * returned values compared to those in the table. An algorithm 
 * matches rows with objects based on one or more keys. Objects 
 * may be missing or in surplus and are so noted.
 * 
 * The fixture processes all the rows of one table following these five steps:
 *
 * - bind the columns to variables and methods by reflection.
 * - query to get the result rows which will be checked.
 * - match the expected and result rows and check the matches.
 * - build html for missing rows.
 * - mark mark missing and surplus rows as such.
 *
 * @version 0.1.0
 * @package FIT
 * @subpackage Fixture
 */
class Testing_FIT_Fixture_Row extends Testing_FIT_Fixture 
{
    protected   $_results   =   array();

   /**
    * bind columsn to fixture and start row iterator
    * 
    * @param object $node
    * @return boolean true on success
    */
    public function doRows( Testing_FIT_Node $node ) 
    {
        $node->next();
        $this->_bind( $node );
        
        $this->_results =   $this->query();
        
        parent::doRows( $node );
        
        // add surplus data to output table
        $parser =   $node->getParser();
        foreach( $this->_results as $plus ) {
            $parser->appendRow( $node->table );
            
            $cells  =   $node->getRowIterator();
            while( $cells->valid() ) {
                $cells->cData   =   $plus[$this->_columnBindings[$cells->key()]['name']];
                $cells->attributes =   array();
                $cells->next();
            }
            $node->markWrong( 'surplus' );
            $node->next();
        }
        
        return true;
    }
    
   /**
    * match cells
    * 
    * Mark matching rows as right  and missing ones as wrong.
    * In case there are some lines left, they are marked as surplus
    * 
    * @param object $node
    * @return boolean true on success
    */
    public function doCells( Testing_FIT_Node $node )
    {
        $this->_current =   array();        
        parent::doCells( $node );
        
        $match  =   true;
        for( $i = 0; $i < count( $this->_results ); ++$i ) {        
            $match  =   true;
            
            foreach( $this->_current as $key => $cData ) {
                if( $this->_results[$i][$key] != $cData ) {
                    $match  =   false;
                    break;
                }
            }
            
            if( $match ) {
                // this line matches
                $node->markRight();
                unset( $this->_results[$i] );
                $this->_results =   array_values( $this->_results );
                return true;
            }
        }
        
        // this line is missing
        $node->markWrong( 'missing' );
        return true;
    }
    
   /**
    * match rows
    * 
    * @param object $node
    * @return boolean true on success
    */
    public function doCell( Testing_FIT_Node $node )
    {
        $no     =   $node->key();
        $bind   =   $this->_columnBindings[$no];
        
        $this->_current[$bind['name']]  =   $this->_columnFilter[$no]->in( $node->cData );
        return true;
    }
    
   /**
    * match rows
    * 
    * @param object $node
    * @return boolean true on success
    */
    protected function _match( Testing_FIT_Node $node )
    {
        while( $node->valid() ) {
        
            $cells  = $node->getRowIterator();
            
            foreach( $cells as $no => $cdata ) {
            
                if( $bind['type'] == 'property' ) {
                    $this->$bind['name']    =   $this->_columnFilter[$no]->in( $cdata );
                    continue;
                }
                $cells->markIgnore();
            }
            $node->next();
        }
        
        return true;
    }    

}
?>