/**
  * 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);
 }
$fsm->reset();
$t->is($fsm->getState(), 'off', '->reset() resets the state');
$t->diag('exit actions');
$fsm->reset();
$exit = new CounterAction();
$fsm->addExitAction('off', $exit);
$fsm->process('push');
$t->is($exit->counter, 1, '->process() calls an exit action when leaving a state');
$fsm->process('push');
$t->is($exit->counter, 1, '->process() does not call an exit action when not leaving the state');
$fsm->process('wait');
$t->is($exit->counter, 1, '->process() does not call an exit action when the state does not change');
$t->diag('entry actions');
$fsm->reset();
$enter = new CounterAction();
$fsm->addEntryAction('broken', $enter);
$fsm->process('smash');
$t->is($enter->counter, 1, '->process() calls an entry action when entering the state');
$fsm->process('wait');
$t->is($enter->counter, 1, '->process() does not call an entry action when the state does not change');
$fsm->process('replace');
$t->is($enter->counter, 1, '->process() does not call an entry action when leaving the state');
$t->diag('transition actions');
$fsm->reset();
$transition = new CounterAction();
$fsm->addTransitionAction('off', 'on', $transition);
$fsm->process('push');
$t->is($transition->counter, 1, '->process() calls transition actions when matching');
$fsm->process('push');
$t->is($transition->counter, 1, '->process() does not call transition actions when they do not match');
$t->diag('input actions');