/**
  * Sets up the finite state machine.
  */
 private function setupFiniteStateMachine()
 {
     $this->fsm = new xfFiniteStateMachine();
     $this->fsm->addStates(array(self::ST_QUERY, self::ST_RANGE_START, self::ST_RANGE_END, self::ST_RANGE_END_INDICATOR, self::ST_RANGE_SEPARATOR, self::ST_ERROR));
     $this->fsm->setInitialState(self::ST_QUERY);
     $this->fsm->addTransitions(array(array(self::ST_QUERY, xfLexemeLucene::WORD, self::ST_QUERY), array(self::ST_QUERY, xfLexemeLucene::SYNTAX, self::ST_QUERY), array(self::ST_QUERY, xfLexemeLucene::PHRASE, self::ST_QUERY), array(self::ST_QUERY, xfLexemeLucene::NUMBER, self::ST_QUERY), array(self::ST_QUERY, xfLexemeLucene::FIELD, self::ST_QUERY), array(self::ST_QUERY, xfLexemeLucene::WILDCARD, self::ST_QUERY)));
     $this->fsm->addTransitions(array(array(self::ST_QUERY, xfLexemeLucene::RANGE_START_INCLUSIVE, self::ST_RANGE_START), array(self::ST_QUERY, xfLexemeLucene::RANGE_START_EXCLUSIVE, self::ST_RANGE_START), array(self::ST_RANGE_START, xfLexemeLucene::WORD, self::ST_RANGE_SEPARATOR), array(self::ST_RANGE_SEPARATOR, xfLexemeLucene::RANGE_SEPARATOR, self::ST_RANGE_END), array(self::ST_RANGE_END, xfLexemeLucene::WORD, self::ST_RANGE_END_INDICATOR), array(self::ST_RANGE_END_INDICATOR, xfLexemeLucene::RANGE_END_INCLUSIVE, self::ST_QUERY), array(self::ST_RANGE_END_INDICATOR, xfLexemeLucene::RANGE_END_EXCLUSIVE, self::ST_QUERY)));
     $addTerm = new xfCriterionBuilderAddTerm($this->builder);
     $addPhrase = new xfCriterionBuilderAddPhrase($this->builder, $this->phraseSlop);
     $addWildcard = new xfCriterionBuilderAddWildcard($this->builder);
     $addField = new xfCriterionBuilderSetField($this->builder);
     $handleSyntax = new xfCriterionBuilderLuceneHandleSyntax($this->builder);
     $handleRange = new xfCriterionBuilderLuceneHandleRange($this->builder);
     $this->fsm->addInputAction(self::ST_QUERY, xfLexemeLucene::WORD, $addTerm);
     $this->fsm->addInputAction(self::ST_QUERY, xfLexemeLucene::PHRASE, $addPhrase);
     $this->fsm->addInputAction(self::ST_QUERY, xfLexemeLucene::WILDCARD, $addWildcard);
     $this->fsm->addInputAction(self::ST_QUERY, xfLexemeLucene::FIELD, $addField);
     $this->fsm->addInputAction(self::ST_QUERY, xfLexemeLucene::SYNTAX, $handleSyntax);
     $this->fsm->addExitAction(self::ST_RANGE_END_INDICATOR, $handleRange);
 }
 /**
  * Sets up the finite state machine according to rules for the Lucene query
  * syntax.
  *
  * This part of the algorithm is derived from the Zend Framework module, 
  * Zend_Search_Lucene.  Some modifications have been made.
  *
  * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  * @license http://framework.zend.com/license/new-bsd New BSD License
  */
 private function setupFiniteStateMachine()
 {
     $this->fsm = new xfFiniteStateMachine();
     // These are our possible states.
     $this->fsm->addStates(array(self::ST_WHITE, self::ST_SYNTAX, self::ST_LEXEME, self::ST_QUOTED, self::ST_ESCAPED_CHAR, self::ST_ESCAPED_QCHAR, self::ST_MODIFIER, self::ST_NUMBER, self::ST_MANTISSA, self::ST_ERROR));
     // Start at this state.
     $this->fsm->setInitialState(self::ST_WHITE);
     // These are the possible transitions.
     $this->fsm->addTransitions(array(array(self::ST_WHITE, self::IN_WHITE, self::ST_WHITE), array(self::ST_WHITE, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_WHITE, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_WHITE, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_CHAR), array(self::ST_WHITE, self::IN_QUOTE, self::ST_QUOTED), array(self::ST_WHITE, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_WHITE, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_WHITE, self::IN_CHAR, self::ST_LEXEME), array(self::ST_WHITE, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_SYNTAX, self::IN_WHITE, self::ST_WHITE), array(self::ST_SYNTAX, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_SYNTAX, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_SYNTAX, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_CHAR), array(self::ST_SYNTAX, self::IN_QUOTE, self::ST_QUOTED), array(self::ST_SYNTAX, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_SYNTAX, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_SYNTAX, self::IN_CHAR, self::ST_LEXEME), array(self::ST_SYNTAX, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_LEXEME, self::IN_WHITE, self::ST_WHITE), array(self::ST_LEXEME, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_LEXEME, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_LEXEME, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_CHAR), array(self::ST_LEXEME, self::IN_QUOTE, self::ST_ERROR), array(self::ST_LEXEME, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_LEXEME, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_LEXEME, self::IN_CHAR, self::ST_LEXEME), array(self::ST_LEXEME, self::IN_MUTABLE, self::ST_LEXEME), array(self::ST_QUOTED, self::IN_WHITE, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_SYNTAX, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_MODIFIER, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_QCHAR), array(self::ST_QUOTED, self::IN_QUOTE, self::ST_WHITE), array(self::ST_QUOTED, self::IN_DECIMAL, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_NUMBER, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_CHAR, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_MUTABLE, self::ST_QUOTED), array(self::ST_ESCAPED_CHAR, self::IN_WHITE, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_SYNTAX, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_MODIFIER, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_ESCAPE_CHAR, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_QUOTE, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_CHAR, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_MUTABLE, self::ST_LEXEME), array(self::ST_ESCAPED_QCHAR, self::IN_WHITE, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_SYNTAX, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_MODIFIER, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_ESCAPE_CHAR, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_QUOTE, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_DECIMAL, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_NUMBER, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_CHAR, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_MUTABLE, self::ST_QUOTED), array(self::ST_MODIFIER, self::IN_WHITE, self::ST_WHITE), array(self::ST_MODIFIER, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_MODIFIER, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_MODIFIER, self::IN_ESCAPE_CHAR, self::ST_ERROR), array(self::ST_MODIFIER, self::IN_QUOTE, self::ST_ERROR), array(self::ST_MODIFIER, self::IN_DECIMAL, self::ST_MANTISSA), array(self::ST_MODIFIER, self::IN_NUMBER, self::ST_NUMBER), array(self::ST_MODIFIER, self::IN_CHAR, self::ST_ERROR), array(self::ST_MODIFIER, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_NUMBER, self::IN_WHITE, self::ST_WHITE), array(self::ST_NUMBER, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_NUMBER, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_NUMBER, self::IN_ESCAPE_CHAR, self::ST_ERROR), array(self::ST_NUMBER, self::IN_QUOTE, self::ST_ERROR), array(self::ST_NUMBER, self::IN_DECIMAL, self::ST_MANTISSA), array(self::ST_NUMBER, self::IN_NUMBER, self::ST_NUMBER), array(self::ST_NUMBER, self::IN_CHAR, self::ST_ERROR), array(self::ST_NUMBER, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_MANTISSA, self::IN_WHITE, self::ST_WHITE), array(self::ST_MANTISSA, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_MANTISSA, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_MANTISSA, self::IN_ESCAPE_CHAR, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_QUOTE, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_DECIMAL, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_NUMBER, self::ST_MANTISSA), array(self::ST_MANTISSA, self::IN_CHAR, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_MUTABLE, self::ST_SYNTAX)));
     // Actions for when an error occurs.
     $quoteWithinError = new xfParserFSMError('A quote within a lexeme is not allowed.');
     $modifierError = new xfParserFSMError('Only a number, white space, or syntax can follow a modifier.');
     $wrongNumberError = new xfParserFSMError('Invalid number syntax: make sure the number only has one decimal point and contains only digits.');
     // Action for adding lexemes.
     $addChar = new xfLexemeBuilderAddChar($this->builder);
     $addLexeme = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::WORD);
     $addModifier = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::SYNTAX);
     $addQuoted = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::PHRASE);
     $addNumber = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::NUMBER);
     $addSyntax = new xfLexemeBuilderLuceneAddSyntax($this->builder);
     // Bind actions for when an error occurs.
     $this->fsm->addInputAction(self::ST_LEXEME, self::IN_QUOTE, $quoteWithinError);
     $this->fsm->addInputAction(self::ST_MODIFIER, self::IN_ESCAPE_CHAR, $modifierError);
     $this->fsm->addInputAction(self::ST_MODIFIER, self::IN_QUOTE, $modifierError);
     $this->fsm->addInputAction(self::ST_MODIFIER, self::IN_CHAR, $modifierError);
     $this->fsm->addInputAction(self::ST_NUMBER, self::IN_ESCAPE_CHAR, $wrongNumberError);
     $this->fsm->addInputAction(self::ST_NUMBER, self::IN_QUOTE, $wrongNumberError);
     $this->fsm->addInputAction(self::ST_NUMBER, self::IN_CHAR, $wrongNumberError);
     $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_ESCAPE_CHAR, $wrongNumberError);
     $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_QUOTE, $wrongNumberError);
     $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_DECIMAL, $wrongNumberError);
     $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_CHAR, $wrongNumberError);
     // Bind actions for adding a character.
     $this->fsm->addEntryAction(self::ST_LEXEME, $addChar);
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_LEXEME, $addChar);
     $this->fsm->addTransitionAction(self::ST_QUOTED, self::ST_QUOTED, $addChar);
     $this->fsm->addTransitionAction(self::ST_ESCAPED_QCHAR, self::ST_QUOTED, $addChar);
     $this->fsm->addEntryAction(self::ST_NUMBER, $addChar);
     $this->fsm->addEntryAction(self::ST_MANTISSA, $addChar);
     $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_NUMBER, $addChar);
     $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_MANTISSA, $addChar);
     // Bind actions for adding a lexeme.
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_WHITE, $addLexeme);
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_SYNTAX, $addLexeme);
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_QUOTED, $addLexeme);
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_MODIFIER, $addLexeme);
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_NUMBER, $addLexeme);
     $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_MANTISSA, $addLexeme);
     // Bind actions for adding a quoted lexeme.
     $this->fsm->addTransitionAction(self::ST_QUOTED, self::ST_WHITE, $addQuoted);
     // Bind actions for adding a number.
     $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_WHITE, $addNumber);
     $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_SYNTAX, $addNumber);
     $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_MODIFIER, $addNumber);
     $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_WHITE, $addNumber);
     $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_SYNTAX, $addNumber);
     $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_MODIFIER, $addNumber);
     // Bind actions for adding a modifier
     $this->fsm->addEntryAction(self::ST_MODIFIER, $addChar);
     $this->fsm->addEntryAction(self::ST_MODIFIER, $addModifier);
     // Bind actions for adding a syntax lexeme.
     $this->fsm->addEntryAction(self::ST_SYNTAX, $addSyntax);
     $this->fsm->addTransitionAction(self::ST_SYNTAX, self::ST_SYNTAX, $addSyntax);
 }
 * file that was distributed with this source code.
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
require 'fsm/xfFiniteStateMachine.class.php';
require 'fsm/xfFiniteStateMachineAction.interface.php';
require 'util/xfException.class.php';
$t = new lime_test(25, new lime_output_color());
class CounterAction implements xfFiniteStateMachineAction
{
    public $counter = 0;
    public function execute()
    {
        $this->counter++;
    }
}
$fsm = new xfFiniteStateMachine(array('on', 'off', 'burned out', 'broken'));
$fsm->setInitialState('off');
$fsm->addTransitions(array(array('on', 'push', 'off'), array('off', 'push', 'on'), array('on', 'smash', 'broken'), array('off', 'smash', 'broken'), array('off', 'wait', 'off'), array('broken', 'replace', 'off'), array('broken', 'wait', 'broken'), array('burned out', 'replace', 'off'), array('on', 'short out', 'burned out')));
$t->diag('->addTransitions(), ->addTransition()');
try {
    $msg = '->addTransitions() fails if array is not a two dimensional array with 3 items in the 2nd dimension';
    $fsm->addTransitions(array('foo'));
    $t->fail($msg);
} catch (Exception $e) {
    $t->pass($msg);
}
try {
    $msg = '->addTransition() fails if the source state does not exist.';
    $fsm->addTransition('exploded', 'implode', 'on');
    $t->fail($msg);
} catch (Exception $e) {