File indexing completed on 2025-01-19 05:21:27

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Search_Lucene
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /** Zend_Search_Lucene_FSMAction */
0023 // require_once 'Zend/Search/Lucene/FSMAction.php';
0024 
0025 /**
0026  * Abstract Finite State Machine
0027  *
0028  * Take a look on Wikipedia state machine description: http://en.wikipedia.org/wiki/Finite_state_machine
0029  *
0030  * Any type of Transducers (Moore machine or Mealy machine) also may be implemented by using this abstract FSM.
0031  * process() methods invokes a specified actions which may construct FSM output.
0032  * Actions may be also used to signal, that we have reached Accept State
0033  *
0034  * @category   Zend
0035  * @package    Zend_Search_Lucene
0036  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0037  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0038  */
0039 abstract class Zend_Search_Lucene_FSM
0040 {
0041     /**
0042      * Machine States alphabet
0043      *
0044      * @var array
0045      */
0046     private $_states = array();
0047 
0048     /**
0049      * Current state
0050      *
0051      * @var integer|string
0052      */
0053     private $_currentState = null;
0054 
0055     /**
0056      * Input alphabet
0057      *
0058      * @var array
0059      */
0060     private $_inputAphabet = array();
0061 
0062     /**
0063      * State transition table
0064      *
0065      * [sourceState][input] => targetState
0066      *
0067      * @var array
0068      */
0069     private $_rules = array();
0070 
0071     /**
0072      * List of entry actions
0073      * Each action executes when entering the state
0074      *
0075      * [state] => action
0076      *
0077      * @var array
0078      */
0079     private $_entryActions =  array();
0080 
0081     /**
0082      * List of exit actions
0083      * Each action executes when exiting the state
0084      *
0085      * [state] => action
0086      *
0087      * @var array
0088      */
0089     private $_exitActions =  array();
0090 
0091     /**
0092      * List of input actions
0093      * Each action executes when entering the state
0094      *
0095      * [state][input] => action
0096      *
0097      * @var array
0098      */
0099     private $_inputActions =  array();
0100 
0101     /**
0102      * List of input actions
0103      * Each action executes when entering the state
0104      *
0105      * [state1][state2] => action
0106      *
0107      * @var array
0108      */
0109     private $_transitionActions =  array();
0110 
0111     /**
0112      * Finite State machine constructor
0113      *
0114      * $states is an array of integers or strings with a list of possible machine states
0115      * constructor treats fist list element as a sturt state (assignes it to $_current state).
0116      * It may be reassigned by setState() call.
0117      * States list may be empty and can be extended later by addState() or addStates() calls.
0118      *
0119      * $inputAphabet is the same as $states, but represents input alphabet
0120      * it also may be extended later by addInputSymbols() or addInputSymbol() calls.
0121      *
0122      * $rules parameter describes FSM transitions and has a structure:
0123      * array( array(sourseState, input, targetState[, inputAction]),
0124      *        array(sourseState, input, targetState[, inputAction]),
0125      *        array(sourseState, input, targetState[, inputAction]),
0126      *        ...
0127      *      )
0128      * Rules also can be added later by addRules() and addRule() calls.
0129      *
0130      * FSM actions are very flexible and may be defined by addEntryAction(), addExitAction(),
0131      * addInputAction() and addTransitionAction() calls.
0132      *
0133      * @param array $states
0134      * @param array $inputAphabet
0135      * @param array $rules
0136      */
0137     public function __construct($states = array(), $inputAphabet = array(), $rules = array())
0138     {
0139         $this->addStates($states);
0140         $this->addInputSymbols($inputAphabet);
0141         $this->addRules($rules);
0142     }
0143 
0144     /**
0145      * Add states to the state machine
0146      *
0147      * @param array $states
0148      */
0149     public function addStates($states)
0150     {
0151         foreach ($states as $state) {
0152             $this->addState($state);
0153         }
0154     }
0155 
0156     /**
0157      * Add state to the state machine
0158      *
0159      * @param integer|string $state
0160      */
0161     public function addState($state)
0162     {
0163         $this->_states[$state] = $state;
0164 
0165         if ($this->_currentState === null) {
0166             $this->_currentState = $state;
0167         }
0168     }
0169 
0170     /**
0171      * Set FSM state.
0172      * No any action is invoked
0173      *
0174      * @param integer|string $state
0175      * @throws Zend_Search_Exception
0176      */
0177     public function setState($state)
0178     {
0179         if (!isset($this->_states[$state])) {
0180             // require_once 'Zend/Search/Exception.php';
0181             throw new Zend_Search_Exception('State \'' . $state . '\' is not on of the possible FSM states.');
0182         }
0183 
0184         $this->_currentState = $state;
0185     }
0186 
0187     /**
0188      * Get FSM state.
0189      *
0190      * @return integer|string $state|null
0191      */
0192     public function getState()
0193     {
0194         return $this->_currentState;
0195     }
0196 
0197     /**
0198      * Add symbols to the input alphabet
0199      *
0200      * @param array $inputAphabet
0201      */
0202     public function addInputSymbols($inputAphabet)
0203     {
0204         foreach ($inputAphabet as $inputSymbol) {
0205             $this->addInputSymbol($inputSymbol);
0206         }
0207     }
0208 
0209     /**
0210      * Add symbol to the input alphabet
0211      *
0212      * @param integer|string $inputSymbol
0213      */
0214     public function addInputSymbol($inputSymbol)
0215     {
0216         $this->_inputAphabet[$inputSymbol] = $inputSymbol;
0217     }
0218 
0219 
0220     /**
0221      * Add transition rules
0222      *
0223      * array structure:
0224      * array( array(sourseState, input, targetState[, inputAction]),
0225      *        array(sourseState, input, targetState[, inputAction]),
0226      *        array(sourseState, input, targetState[, inputAction]),
0227      *        ...
0228      *      )
0229      *
0230      * @param array $rules
0231      */
0232     public function addRules($rules)
0233     {
0234         foreach ($rules as $rule) {
0235             $this->addrule($rule[0], $rule[1], $rule[2], isset($rule[3])?$rule[3]:null);
0236         }
0237     }
0238 
0239     /**
0240      * Add symbol to the input alphabet
0241      *
0242      * @param integer|string $sourceState
0243      * @param integer|string $input
0244      * @param integer|string $targetState
0245      * @param Zend_Search_Lucene_FSMAction|null $inputAction
0246      * @throws Zend_Search_Exception
0247      */
0248     public function addRule($sourceState, $input, $targetState, $inputAction = null)
0249     {
0250         if (!isset($this->_states[$sourceState])) {
0251             // require_once 'Zend/Search/Exception.php';
0252             throw new Zend_Search_Exception('Undefined source state (' . $sourceState . ').');
0253         }
0254         if (!isset($this->_states[$targetState])) {
0255             // require_once 'Zend/Search/Exception.php';
0256             throw new Zend_Search_Exception('Undefined target state (' . $targetState . ').');
0257         }
0258         if (!isset($this->_inputAphabet[$input])) {
0259             // require_once 'Zend/Search/Exception.php';
0260             throw new Zend_Search_Exception('Undefined input symbol (' . $input . ').');
0261         }
0262 
0263         if (!isset($this->_rules[$sourceState])) {
0264             $this->_rules[$sourceState] = array();
0265         }
0266         if (isset($this->_rules[$sourceState][$input])) {
0267             // require_once 'Zend/Search/Exception.php';
0268             throw new Zend_Search_Exception('Rule for {state,input} pair (' . $sourceState . ', '. $input . ') is already defined.');
0269         }
0270 
0271         $this->_rules[$sourceState][$input] = $targetState;
0272 
0273 
0274         if ($inputAction !== null) {
0275             $this->addInputAction($sourceState, $input, $inputAction);
0276         }
0277     }
0278 
0279 
0280     /**
0281      * Add state entry action.
0282      * Several entry actions are allowed.
0283      * Action execution order is defined by addEntryAction() calls
0284      *
0285      * @param integer|string $state
0286      * @param Zend_Search_Lucene_FSMAction $action
0287      */
0288     public function addEntryAction($state, Zend_Search_Lucene_FSMAction $action)
0289     {
0290         if (!isset($this->_states[$state])) {
0291             // require_once 'Zend/Search/Exception.php';
0292             throw new Zend_Search_Exception('Undefined state (' . $state. ').');
0293         }
0294 
0295         if (!isset($this->_entryActions[$state])) {
0296             $this->_entryActions[$state] = array();
0297         }
0298 
0299         $this->_entryActions[$state][] = $action;
0300     }
0301 
0302     /**
0303      * Add state exit action.
0304      * Several exit actions are allowed.
0305      * Action execution order is defined by addEntryAction() calls
0306      *
0307      * @param integer|string $state
0308      * @param Zend_Search_Lucene_FSMAction $action
0309      */
0310     public function addExitAction($state, Zend_Search_Lucene_FSMAction $action)
0311     {
0312         if (!isset($this->_states[$state])) {
0313             // require_once 'Zend/Search/Exception.php';
0314             throw new Zend_Search_Exception('Undefined state (' . $state. ').');
0315         }
0316 
0317         if (!isset($this->_exitActions[$state])) {
0318             $this->_exitActions[$state] = array();
0319         }
0320 
0321         $this->_exitActions[$state][] = $action;
0322     }
0323 
0324     /**
0325      * Add input action (defined by {state, input} pair).
0326      * Several input actions are allowed.
0327      * Action execution order is defined by addInputAction() calls
0328      *
0329      * @param integer|string $state
0330      * @param integer|string $input
0331      * @param Zend_Search_Lucene_FSMAction $action
0332      */
0333     public function addInputAction($state, $inputSymbol, Zend_Search_Lucene_FSMAction $action)
0334     {
0335         if (!isset($this->_states[$state])) {
0336             // require_once 'Zend/Search/Exception.php';
0337             throw new Zend_Search_Exception('Undefined state (' . $state. ').');
0338         }
0339         if (!isset($this->_inputAphabet[$inputSymbol])) {
0340             // require_once 'Zend/Search/Exception.php';
0341             throw new Zend_Search_Exception('Undefined input symbol (' . $inputSymbol. ').');
0342         }
0343 
0344         if (!isset($this->_inputActions[$state])) {
0345             $this->_inputActions[$state] = array();
0346         }
0347         if (!isset($this->_inputActions[$state][$inputSymbol])) {
0348             $this->_inputActions[$state][$inputSymbol] = array();
0349         }
0350 
0351         $this->_inputActions[$state][$inputSymbol][] = $action;
0352     }
0353 
0354     /**
0355      * Add transition action (defined by {state, input} pair).
0356      * Several transition actions are allowed.
0357      * Action execution order is defined by addTransitionAction() calls
0358      *
0359      * @param integer|string $sourceState
0360      * @param integer|string $targetState
0361      * @param Zend_Search_Lucene_FSMAction $action
0362      */
0363     public function addTransitionAction($sourceState, $targetState, Zend_Search_Lucene_FSMAction $action)
0364     {
0365         if (!isset($this->_states[$sourceState])) {
0366             // require_once 'Zend/Search/Exception.php';
0367             throw new Zend_Search_Exception('Undefined source state (' . $sourceState. ').');
0368         }
0369         if (!isset($this->_states[$targetState])) {
0370             // require_once 'Zend/Search/Exception.php';
0371             throw new Zend_Search_Exception('Undefined source state (' . $targetState. ').');
0372         }
0373 
0374         if (!isset($this->_transitionActions[$sourceState])) {
0375             $this->_transitionActions[$sourceState] = array();
0376         }
0377         if (!isset($this->_transitionActions[$sourceState][$targetState])) {
0378             $this->_transitionActions[$sourceState][$targetState] = array();
0379         }
0380 
0381         $this->_transitionActions[$sourceState][$targetState][] = $action;
0382     }
0383 
0384 
0385     /**
0386      * Process an input
0387      *
0388      * @param mixed $input
0389      * @throws Zend_Search_Exception
0390      */
0391     public function process($input)
0392     {
0393         if (!isset($this->_rules[$this->_currentState])) {
0394             // require_once 'Zend/Search/Exception.php';
0395             throw new Zend_Search_Exception('There is no any rule for current state (' . $this->_currentState . ').');
0396         }
0397         if (!isset($this->_rules[$this->_currentState][$input])) {
0398             // require_once 'Zend/Search/Exception.php';
0399             throw new Zend_Search_Exception('There is no any rule for {current state, input} pair (' . $this->_currentState . ', ' . $input . ').');
0400         }
0401 
0402         $sourceState = $this->_currentState;
0403         $targetState = $this->_rules[$this->_currentState][$input];
0404 
0405         if ($sourceState != $targetState  &&  isset($this->_exitActions[$sourceState])) {
0406             foreach ($this->_exitActions[$sourceState] as $action) {
0407                 $action->doAction();
0408             }
0409         }
0410         if (isset($this->_inputActions[$sourceState]) &&
0411             isset($this->_inputActions[$sourceState][$input])) {
0412             foreach ($this->_inputActions[$sourceState][$input] as $action) {
0413                 $action->doAction();
0414             }
0415         }
0416 
0417 
0418         $this->_currentState = $targetState;
0419 
0420         if (isset($this->_transitionActions[$sourceState]) &&
0421             isset($this->_transitionActions[$sourceState][$targetState])) {
0422             foreach ($this->_transitionActions[$sourceState][$targetState] as $action) {
0423                 $action->doAction();
0424             }
0425         }
0426         if ($sourceState != $targetState  &&  isset($this->_entryActions[$targetState])) {
0427             foreach ($this->_entryActions[$targetState] as $action) {
0428                 $action->doAction();
0429             }
0430         }
0431     }
0432 
0433     public function reset()
0434     {
0435         if (count($this->_states) == 0) {
0436             // require_once 'Zend/Search/Exception.php';
0437             throw new Zend_Search_Exception('There is no any state defined for FSM.');
0438         }
0439 
0440         $this->_currentState = $this->_states[0];
0441     }
0442 }
0443