<?php
/**
 * FIT Parser Node Iterator
 * 
 * $Id$
 * 
 * @author gERD Schaufelberger <gerd@php-tools.net>
 * @package FIT
 * @subpackage Parser
 * @license LGPL http://www.gnu.org/copyleft/lesser.html
 */
 
/**
 * FIT Parser Node Iterator
 * 
 * @version 0.2.0
 * @package FIT
 * @subpackage Fixture
 */
class Testing_FIT_Node implements Iterator
{
   
/**
    * make the include folder available for user's fixtures
    * @var array
    */
    
private static $_markAttributes  =   array(
                                    
'ignore'    => array( 'style' => 'background-color:#EFEFEF;' ),
                                    
'right'     => array( 'style' => 'background-color:#CFFFCF;'),
                                    
'wrong'     => array( 'style' => 'background-color:#FFCFCF;' ),
                                    
'error'     => array( 'style' => 'background-color:#FFFFCF;' ),
                                    
'exception' => array( 'style' => 'background-color:#FFFFCF;' ),
                                    
'label'     => array( 'style' => 'color:#C08080;font-style:italic;font-size:small;' )
                                    );

   
/**
    * parser holding tables
    * @var object
    */
    
private $_parser null;
    
   
/**
    * current table
    * @var int
    */
    
private $_table null;
    
   
/**
    * current row in table
    * @var int
    */
    
private $_row null;

   
/**
    * current cel in row
    * @var int
    */
    
private $_col null;

   
/**
    * current pointer position
    * @var int
    */
    
private $_pointer null;

   
/**
    * contain the time measurement
    * [startTest]       => start time of whole test
    * [startTestFunc]   => start time test function
    * [endTestFunc]     => end time test function
    * [elapsedTime]     => elapsed time of test function
    * @var array
    */
    
private static $_time = array(
                        
'start'     => -1,
                        
'funcStart' => -1,
                        
'funcEnd'   => -1,
                        
'elapsed'   => -1
                          
);

   
/**
    * summary parser for fit.summary
    * @var object
    */
    
private static $_summaryParser null;

   
/**
    * initialize node
    *
    * Initialize node iterator. Choose a row in case you want to iterate columns
    * 
    * @param object $parser
    * @param int $table index of table
    * @param int $row index of row in table
    */
    
public function __construct$parser$table$row null )
    {
        
$this->_parser  $parser;
        
$this->_table   $table;
        
$this->_row     $row;
        
$this->_pointer 0;
        
        
// copy parser
        
if( !self::$_summaryParser ) {
            
self::$_summaryParser = clone $this->_parser;
       }

    }

   
/**
    * Receive a node object that allows to iterate throuch columns
    *
    * @return object $node
    */
    
public function getRowIterator() 
    {
        if( 
$this->_row !== null ) {
            return 
null;
        }
        
$node = new Testing_FIT_Node$this->_parser$this->_table$this->_pointer );

        if( 
self::$_time['start'] == -) {
            
self::$_time['start'] = microtimetrue );
        }

        return 
$node;
    }
    
   
/**
    * magic getter function
    * 
    * Simple access to "cData", "attributes" and "tag" and node position
    *
    * @param string $name of property
    * @return mixed $value
    */
    
public function __get$name 
    {
        
// node position
        
if( $name == 'table' ) {
            return 
$this->_table;
        }
        
        if( 
$name == 'row' ) {
            if( 
$this->_row === null ) {
                return 
$this->_pointer;
            }
            return 
$this->_row;
        }
        
        if( 
$name == 'column' ) {
            return 
$this->_pointer;
        }
    
        
// normal node properties
        
if( !in_array$name, array( 'tag''cData''attributes' ) ) ) {
            return 
null;
        }
    
        if( 
$this->_row === null ) {
            
$node   =& $this->_parser->getNodeValue$name$this->_table$this->_pointer );
        }
        else {
            
$node   =& $this->_parser->getNodeValue$name$this->_table$this->_row$this->_pointer );
        }
        
        return 
$node;
    }
    
   
/**
    * magic setter function
    *
    * Simple access to "cData", "attributes" and "tag"
    *
    * @param string $name of property
    * @param mixed $value  
    * @return void   
    */
    
public function __set$name$value 
    {
        if( !
in_array$name, array( 'tag''cData''attributes' ) ) ) {
            return;
        }
        if( 
$this->_row === null ) {
            
$node   =&  $this->_parser->getNodeValue$name$this->_table$this->_pointer );
        }
        else {
            
$node   =&  $this->_parser->getNodeValue$name$this->_table$this->_row$this->_pointer );
        }
        
        
$node   $value;
        return;
    }
    
   
/**
    * Mark node: right
    *
    * @return bool true on success
    */
    
public function markRight() 
    {
        
$this->_setSummaryInfo'right' );
        return 
$this->mark'right' );
    }

   
/**
    * Mark node: wrong
    *
    * @param mixed $actual value
    * @return bool true on success
    */
    
public function markWrong$actual null 
    {
        if( !
$actual ) {
            
$this->_setSummaryInfo'wrong' );
            return 
$this->mark'wrong' );    
        }
        
        
$escape =   $this->_escape$actual );
        
$this->_setSummaryInfo'wrong'$msg );
        
$msg    =   $this->_label'expected' ) . '<hr />' $escape $this->_label'actual' );
        return 
$this->mark'wrong'$msg );
    }
    
   
/**
    * Mark node: error
    *
    * @param string $msg optional error message
    * @return bool true on success
    */
    
public function markError$msg null 
    {
        
$cData  =   $this->_parser->getNodeValue'cData'$this->_table$this->_row$this->_pointer );
        if( 
$cData == 'error' ) {
            return 
$this->markRight();
        }

        if( !empty( 
$msg ) ) {
            
$msg    =   '<hr />' $msg;
        }

        
$this->_setSummaryInfo'error'$msg );
        
$this->mark'error'$msg );
        return 
true;
    }
    
   
/**
    * Mark node: exception happend
    *
    * @param object $e exeption
    * @return bool true on success
    */
    
public function markException$e 
    {
        
$msg    =   $e->getMessage();
        
$this->_setSummaryInfo'exception'$msg );
        
$msg    '<hr />' $msg;
        return 
$this->mark'exception'$msg );
    }
    
   
/**
    * Mark node: ignored
    *
    * @param object $node
    * @return bool true on success
    */
    
public function markIgnore() 
    {
        
$this->_setSummaryInfo'ignore' );
        
$this->mark'ignore' );
        return 
true;
    }
    
   
/**
    * Mark node: info
    *
    * @param string $msg optional error message
    * @return bool true on success
    */
    
public function markInfo$msg null 
    {
        
$this->_setSummaryInfo'info'$msg );
        if( !empty( 
$msg ) ) {
            
$msg    ='<hr />' $msg;
        }
        
        
$this->mark'info'$msg );
        return 
true;
    }
   
   
/**
    * Mark node: label
    *
    * @param string $msg optional error message
    * @return bool true on success
    */
    
public function markLabel$msg null 
    {
        
$msg    $this->_label$msg );
        return 
$this->marknull$msg );
    }
    
   
/**
    * Mark this node
    *
    * @param string $att attribute set to mark with, or null if no attributes should be added at all
    * @param string $msg additional message
    * @return string 
    */
    
public function mark$att$msg null 
    {
        if( 
$this->_row === null ) {
            
$cells  =   $this->getRowIterator();
            while( 
$cells->valid() ) {
                
$cells->mark$att$msg );
                
$cells->next();
            }
            return 
true;
        } 
        
        
// or a single cell
        
$atts   =& $this->_parser->getNodeValue'attributes'$this->_table$this->_row$this->_pointer );
        
$atts   array_merge$attsself::$_markAttributes[$att] );

        if( 
$msg === null ) {
            return 
true;
        }

        
$cData  =& $this->_parser->getNodeValue'cData'$this->_table$this->_row$this->_pointer );
        
$cData  .= $msg;

        return 
true;
    }
   
   
/**
    * Turn string into label
    *
    * @param string $msg label message
    * @return string 
    */
    
private function _label$msg null 
    {
        if( !
strlen$msg ) ) {
            return 
'';
        }
        
        
$atts   =   array();
        foreach( 
self::$_markAttributes['label'] as $name => $value ) {
            
array_push$atts$name '="' $value  '"' );
        }
        return 
sprintf'<span %s>%s</span>'implode' '$atts ), $this->_escape$msg ) );
    }
    
   
/**
    * HTML style string escape
    *
    * @param string $msg string to be escaped
    * @return string 
    */
    
private function _escape$msg null 
    {
        if( 
$msg === null ) {
            return 
'null';
        }
        if( 
$msg === true ) {
            return 
'true';
        }
        if( 
$msg === false ) {
            return 
'false';
        }
        if( !
strlen$msg ) ) {
            return 
'';
        }
        return 
htmlspecialchars$msg );
    }
   
   
/**
    * Iterator interface: current
    * 
    * @return mixed $data cDate in case of column, amount of children in case of row
    */
    
public function current() 
    {
        if( 
$this->_row === null ) {
            
// iterate through rows
            
return $this->_parser->countChildNodes$this->_table$this->_pointer );
        }
        
        
// iterate through columns
        
return $this->_parser->getNodeValue'cData'$this->_table$this->_row$this->_pointer );
    }
    
   
/**
    * Iterator interface: next
    * 
    * Move pointer to next entry
    * @return void
    */
    
public function next() 
    {
        ++
$this->_pointer;
    }
    
   
/**
    * Iterator interface: key
    * 
    * Receive internal pointer
    * @return int $pointer
    */
    
public function key() 
    {
        return 
$this->_pointer;
    }
    
   
/**
    * Iterator interface: valid
    * 
    * Check whether current pointer is still in range
    * @return void
    */
    
public function valid() 
    {
        if( 
$this->_row === null ) {
            
// iterate through rows
            
if( $this->_pointer $this->_parser->countChildNodes$this->_table ) ) {
                return 
true;
            }            
            return 
false;
        }
        
        
// iterate through columns
        
if( $this->_pointer $this->_parser->countChildNodes$this->_table$this->_row ) ) {
            return 
true;
        }            
        return 
false;
    }
    
   
/**
    * Iterator interface: rewind
    * 
    * Reset pointer
    * @return void
    */
    
public function rewind() 
    {
        
$this->_pointer 0;
    }

   
/**
    * set time function 
    *
    * get duration of testing a function
    * @return void
    */
    
private function _setTime()
    {
        
self::$_time['funcEnd'] = microtimetrue );
        if( 
self::$_time['funcStart'] == -) {
            
self::$_time['funcStart'] = self::$_time['start'];
        }

        
self::$_time['elapsed']     = self::$_time['funcEnd'] - self::$_time['funcStart'];
        
self::$_time['funcStart']   = self::$_time['funcEnd'];
    }

   
/**
    * set summary information
    * 
    * write to $_summaryParser info data for summary
    * @param string $type one of the types used in mark: "error", "exception", "ignore",
    * @param string $msg optional message 
    * @param array $msg     contain some information of testresult
    *                       'cData'     of cell
    *                       'message'   can be right, wrong, ignore, exception
    *                       'error'     error-message
    *                       'exception' exception-message
    *                       'time'      duration of test
    * @return bool true on usccess
    */
    
private function _setSummaryInfo$type$msg null )
    {
        
$this->_setTime();

        
// append rows if required
        
$counter    =   $this->_row;
        if( 
$this->_row == null ) {
            
$counter    =   $this->_pointer;
        }
        if( 
$counter >= self::$_summaryParser->countChildNodes$this->_table ) ) {
            
self::$_summaryParser->appendRow$this->_table );
        }

        
// to mark as summary table
        
$summaryVal self::$_summaryParser->getNodeValue'attributes'$this->_table0);
        if( empty( 
$summaryVal['noSummary'] ) ) {
            
$attrib $summaryVal;
            
$attrib['noSummary'] = true;
            
self::$_summaryParser->setNodeValue'attributes'$attrib$this->_table0);
        }

        
$cData  =   null;
        if( 
$this->_row !== null ) {
            
$this->_parser->getNodeValue'cData'$this->_table$this->_row$this->_pointer );
        }
        
        
$results    =   array(
                
'cData'     =>  $cData,
                
'type'      =>  $type,
                
'message'   =>  $msg,
                
'time'      =>  array(
                                    
'funcStart' => self::$_time['funcStart'],
                                    
'funcEnd'   => self::$_time['funcEnd'],
                                    ),
                );
        
        
$data   =   array(
                    
'attributes'    =>  self::$_markAttributes[$type]['style'],
                    
'results'       =>  $results
                    
);
        
        if( 
$this->_row !== null ) {
            
self::$_summaryParser->setNodeValue'attributes'$data$this->_table$this->_row$this->_pointer );
        } else {
            
self::$_summaryParser->setNodeValue'attributes'$data$this->_table$this->_pointer );
        }
        return 
true;
    }

   
/**
    * get summary parser
    *
    * public function for summary fixture to get summary parser
    * @see Summary.php
    * @return object $_summaryParser
    */
    
public function getSummary()
    {
        return 
self::$_summaryParser;
    }

   
/**
    * get parser
    *
    * public function for summary fixture to get parser
    * @see Summary.php
    * @return object $_parser
    */
    
public function getParser()
    {
        return 
$this->_parser;
    }
}
?>