File indexing completed on 2024-06-23 05:55:39

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  * @subpackage Search
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 
0024 /** Internally used classes */
0025 
0026 /** Zend_Search_Lucene_Analysis_Analyzer */
0027 // require_once 'Zend/Search/Lucene/Analysis/Analyzer.php';
0028 
0029 /** Zend_Search_Lucene_Search_QueryToken */
0030 // require_once 'Zend/Search/Lucene/Search/QueryToken.php';
0031 
0032 
0033 
0034 /** Zend_Search_Lucene_FSM */
0035 // require_once 'Zend/Search/Lucene/FSM.php';
0036 
0037 /**
0038  * @category   Zend
0039  * @package    Zend_Search_Lucene
0040  * @subpackage Search
0041  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0042  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0043  */
0044 class Zend_Search_Lucene_Search_QueryParser extends Zend_Search_Lucene_FSM
0045 {
0046     /**
0047      * Parser instance
0048      *
0049      * @var Zend_Search_Lucene_Search_QueryParser
0050      */
0051     private static $_instance = null;
0052 
0053 
0054     /**
0055      * Query lexer
0056      *
0057      * @var Zend_Search_Lucene_Search_QueryLexer
0058      */
0059     private $_lexer;
0060 
0061     /**
0062      * Tokens list
0063      * Array of Zend_Search_Lucene_Search_QueryToken objects
0064      *
0065      * @var array
0066      */
0067     private $_tokens;
0068 
0069     /**
0070      * Current token
0071      *
0072      * @var integer|string
0073      */
0074     private $_currentToken;
0075 
0076     /**
0077      * Last token
0078      *
0079      * It can be processed within FSM states, but this addirional state simplifies FSM
0080      *
0081      * @var Zend_Search_Lucene_Search_QueryToken
0082      */
0083     private $_lastToken = null;
0084 
0085     /**
0086      * Range query first term
0087      *
0088      * @var string
0089      */
0090     private $_rqFirstTerm = null;
0091 
0092     /**
0093      * Current query parser context
0094      *
0095      * @var Zend_Search_Lucene_Search_QueryParserContext
0096      */
0097     private $_context;
0098 
0099     /**
0100      * Context stack
0101      *
0102      * @var array
0103      */
0104     private $_contextStack;
0105 
0106     /**
0107      * Query string encoding
0108      *
0109      * @var string
0110      */
0111     private $_encoding;
0112 
0113     /**
0114      * Query string default encoding
0115      *
0116      * @var string
0117      */
0118     private $_defaultEncoding = '';
0119 
0120     /**
0121      * Defines query parsing mode.
0122      *
0123      * If this option is turned on, then query parser suppress query parser exceptions
0124      * and constructs multi-term query using all words from a query.
0125      *
0126      * That helps to avoid exceptions caused by queries, which don't conform to query language,
0127      * but limits possibilities to check, that query entered by user has some inconsistencies.
0128      *
0129      *
0130      * Default is true.
0131      *
0132      * Use {@link Zend_Search_Lucene::suppressQueryParsingExceptions()},
0133      * {@link Zend_Search_Lucene::dontSuppressQueryParsingExceptions()} and
0134      * {@link Zend_Search_Lucene::checkQueryParsingExceptionsSuppressMode()} to operate
0135      * with this setting.
0136      *
0137      * @var boolean
0138      */
0139     private $_suppressQueryParsingExceptions = true;
0140 
0141     /**
0142      * Boolean operators constants
0143      */
0144     const B_OR  = 0;
0145     const B_AND = 1;
0146 
0147     /**
0148      * Default boolean queries operator
0149      *
0150      * @var integer
0151      */
0152     private $_defaultOperator = self::B_OR;
0153 
0154 
0155     /** Query parser State Machine states */
0156     const ST_COMMON_QUERY_ELEMENT       = 0;   // Terms, phrases, operators
0157     const ST_CLOSEDINT_RQ_START         = 1;   // Range query start (closed interval) - '['
0158     const ST_CLOSEDINT_RQ_FIRST_TERM    = 2;   // First term in '[term1 to term2]' construction
0159     const ST_CLOSEDINT_RQ_TO_TERM       = 3;   // 'TO' lexeme in '[term1 to term2]' construction
0160     const ST_CLOSEDINT_RQ_LAST_TERM     = 4;   // Second term in '[term1 to term2]' construction
0161     const ST_CLOSEDINT_RQ_END           = 5;   // Range query end (closed interval) - ']'
0162     const ST_OPENEDINT_RQ_START         = 6;   // Range query start (opened interval) - '{'
0163     const ST_OPENEDINT_RQ_FIRST_TERM    = 7;   // First term in '{term1 to term2}' construction
0164     const ST_OPENEDINT_RQ_TO_TERM       = 8;   // 'TO' lexeme in '{term1 to term2}' construction
0165     const ST_OPENEDINT_RQ_LAST_TERM     = 9;   // Second term in '{term1 to term2}' construction
0166     const ST_OPENEDINT_RQ_END           = 10;  // Range query end (opened interval) - '}'
0167 
0168     /**
0169      * Parser constructor
0170      */
0171     public function __construct()
0172     {
0173         parent::__construct(array(self::ST_COMMON_QUERY_ELEMENT,
0174                                   self::ST_CLOSEDINT_RQ_START,
0175                                   self::ST_CLOSEDINT_RQ_FIRST_TERM,
0176                                   self::ST_CLOSEDINT_RQ_TO_TERM,
0177                                   self::ST_CLOSEDINT_RQ_LAST_TERM,
0178                                   self::ST_CLOSEDINT_RQ_END,
0179                                   self::ST_OPENEDINT_RQ_START,
0180                                   self::ST_OPENEDINT_RQ_FIRST_TERM,
0181                                   self::ST_OPENEDINT_RQ_TO_TERM,
0182                                   self::ST_OPENEDINT_RQ_LAST_TERM,
0183                                   self::ST_OPENEDINT_RQ_END
0184                                  ),
0185                             Zend_Search_Lucene_Search_QueryToken::getTypes());
0186 
0187         $this->addRules(
0188              array(array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_WORD,             self::ST_COMMON_QUERY_ELEMENT),
0189                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PHRASE,           self::ST_COMMON_QUERY_ELEMENT),
0190                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FIELD,            self::ST_COMMON_QUERY_ELEMENT),
0191                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED,         self::ST_COMMON_QUERY_ELEMENT),
0192                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED,       self::ST_COMMON_QUERY_ELEMENT),
0193                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK,  self::ST_COMMON_QUERY_ELEMENT),
0194                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_BOOSTING_MARK,    self::ST_COMMON_QUERY_ELEMENT),
0195                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_INCL_START, self::ST_CLOSEDINT_RQ_START),
0196                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_EXCL_START, self::ST_OPENEDINT_RQ_START),
0197                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_START,   self::ST_COMMON_QUERY_ELEMENT),
0198                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_END,     self::ST_COMMON_QUERY_ELEMENT),
0199                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME,       self::ST_COMMON_QUERY_ELEMENT),
0200                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME,        self::ST_COMMON_QUERY_ELEMENT),
0201                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME,       self::ST_COMMON_QUERY_ELEMENT),
0202                    array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NUMBER,           self::ST_COMMON_QUERY_ELEMENT)
0203                   ));
0204         $this->addRules(
0205              array(array(self::ST_CLOSEDINT_RQ_START,      Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_CLOSEDINT_RQ_FIRST_TERM),
0206                    array(self::ST_CLOSEDINT_RQ_FIRST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_TO_LEXEME,      self::ST_CLOSEDINT_RQ_TO_TERM),
0207                    array(self::ST_CLOSEDINT_RQ_TO_TERM,    Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_CLOSEDINT_RQ_LAST_TERM),
0208                    array(self::ST_CLOSEDINT_RQ_LAST_TERM,  Zend_Search_Lucene_Search_QueryToken::TT_RANGE_INCL_END, self::ST_COMMON_QUERY_ELEMENT)
0209                   ));
0210         $this->addRules(
0211              array(array(self::ST_OPENEDINT_RQ_START,      Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_OPENEDINT_RQ_FIRST_TERM),
0212                    array(self::ST_OPENEDINT_RQ_FIRST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_TO_LEXEME,      self::ST_OPENEDINT_RQ_TO_TERM),
0213                    array(self::ST_OPENEDINT_RQ_TO_TERM,    Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_OPENEDINT_RQ_LAST_TERM),
0214                    array(self::ST_OPENEDINT_RQ_LAST_TERM,  Zend_Search_Lucene_Search_QueryToken::TT_RANGE_EXCL_END, self::ST_COMMON_QUERY_ELEMENT)
0215                   ));
0216 
0217 
0218 
0219         $addTermEntryAction             = new Zend_Search_Lucene_FSMAction($this, 'addTermEntry');
0220         $addPhraseEntryAction           = new Zend_Search_Lucene_FSMAction($this, 'addPhraseEntry');
0221         $setFieldAction                 = new Zend_Search_Lucene_FSMAction($this, 'setField');
0222         $setSignAction                  = new Zend_Search_Lucene_FSMAction($this, 'setSign');
0223         $setFuzzyProxAction             = new Zend_Search_Lucene_FSMAction($this, 'processFuzzyProximityModifier');
0224         $processModifierParameterAction = new Zend_Search_Lucene_FSMAction($this, 'processModifierParameter');
0225         $subqueryStartAction            = new Zend_Search_Lucene_FSMAction($this, 'subqueryStart');
0226         $subqueryEndAction              = new Zend_Search_Lucene_FSMAction($this, 'subqueryEnd');
0227         $logicalOperatorAction          = new Zend_Search_Lucene_FSMAction($this, 'logicalOperator');
0228         $openedRQFirstTermAction        = new Zend_Search_Lucene_FSMAction($this, 'openedRQFirstTerm');
0229         $openedRQLastTermAction         = new Zend_Search_Lucene_FSMAction($this, 'openedRQLastTerm');
0230         $closedRQFirstTermAction        = new Zend_Search_Lucene_FSMAction($this, 'closedRQFirstTerm');
0231         $closedRQLastTermAction         = new Zend_Search_Lucene_FSMAction($this, 'closedRQLastTerm');
0232 
0233 
0234         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_WORD,            $addTermEntryAction);
0235         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PHRASE,          $addPhraseEntryAction);
0236         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FIELD,           $setFieldAction);
0237         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED,        $setSignAction);
0238         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED,      $setSignAction);
0239         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK, $setFuzzyProxAction);
0240         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NUMBER,          $processModifierParameterAction);
0241         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_START,  $subqueryStartAction);
0242         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_END,    $subqueryEndAction);
0243         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME,      $logicalOperatorAction);
0244         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME,       $logicalOperatorAction);
0245         $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME,      $logicalOperatorAction);
0246 
0247         $this->addEntryAction(self::ST_OPENEDINT_RQ_FIRST_TERM, $openedRQFirstTermAction);
0248         $this->addEntryAction(self::ST_OPENEDINT_RQ_LAST_TERM,  $openedRQLastTermAction);
0249         $this->addEntryAction(self::ST_CLOSEDINT_RQ_FIRST_TERM, $closedRQFirstTermAction);
0250         $this->addEntryAction(self::ST_CLOSEDINT_RQ_LAST_TERM,  $closedRQLastTermAction);
0251 
0252 
0253         // require_once 'Zend/Search/Lucene/Search/QueryLexer.php';
0254         $this->_lexer = new Zend_Search_Lucene_Search_QueryLexer();
0255     }
0256 
0257     /**
0258      * Get query parser instance
0259      *
0260      * @return Zend_Search_Lucene_Search_QueryParser
0261      */
0262     private static function _getInstance()
0263     {
0264         if (self::$_instance === null) {
0265             self::$_instance = new self();
0266         }
0267         return self::$_instance;
0268     }
0269 
0270     /**
0271      * Set query string default encoding
0272      *
0273      * @param string $encoding
0274      */
0275     public static function setDefaultEncoding($encoding)
0276     {
0277         self::_getInstance()->_defaultEncoding = $encoding;
0278     }
0279 
0280     /**
0281      * Get query string default encoding
0282      *
0283      * @return string
0284      */
0285     public static function getDefaultEncoding()
0286     {
0287        return self::_getInstance()->_defaultEncoding;
0288     }
0289 
0290     /**
0291      * Set default boolean operator
0292      *
0293      * @param integer $operator
0294      */
0295     public static function setDefaultOperator($operator)
0296     {
0297         self::_getInstance()->_defaultOperator = $operator;
0298     }
0299 
0300     /**
0301      * Get default boolean operator
0302      *
0303      * @return integer
0304      */
0305     public static function getDefaultOperator()
0306     {
0307         return self::_getInstance()->_defaultOperator;
0308     }
0309 
0310     /**
0311      * Turn on 'suppress query parser exceptions' mode.
0312      */
0313     public static function suppressQueryParsingExceptions()
0314     {
0315         self::_getInstance()->_suppressQueryParsingExceptions = true;
0316     }
0317     /**
0318      * Turn off 'suppress query parser exceptions' mode.
0319      */
0320     public static function dontSuppressQueryParsingExceptions()
0321     {
0322         self::_getInstance()->_suppressQueryParsingExceptions = false;
0323     }
0324     /**
0325      * Check 'suppress query parser exceptions' mode.
0326      * @return boolean
0327      */
0328     public static function queryParsingExceptionsSuppressed()
0329     {
0330         return self::_getInstance()->_suppressQueryParsingExceptions;
0331     }
0332 
0333 
0334     /**
0335      * Escape keyword to force it to be parsed as one term
0336      *
0337      * @param string $keyword
0338      * @return string
0339      */
0340     public static function escape($keyword)
0341     {
0342         return '\\' . implode('\\', str_split($keyword));
0343     }
0344 
0345     /**
0346      * Parses a query string
0347      *
0348      * @param string $strQuery
0349      * @param string $encoding
0350      * @return Zend_Search_Lucene_Search_Query
0351      * @throws Zend_Search_Lucene_Search_QueryParserException
0352      */
0353     public static function parse($strQuery, $encoding = null)
0354     {
0355         self::_getInstance();
0356 
0357         // Reset FSM if previous parse operation didn't return it into a correct state
0358         self::$_instance->reset();
0359 
0360         // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0361         try {
0362             // require_once 'Zend/Search/Lucene/Search/QueryParserContext.php';
0363 
0364             self::$_instance->_encoding     = ($encoding !== null) ? $encoding : self::$_instance->_defaultEncoding;
0365             self::$_instance->_lastToken    = null;
0366             self::$_instance->_context      = new Zend_Search_Lucene_Search_QueryParserContext(self::$_instance->_encoding);
0367             self::$_instance->_contextStack = array();
0368             self::$_instance->_tokens       = self::$_instance->_lexer->tokenize($strQuery, self::$_instance->_encoding);
0369 
0370             // Empty query
0371             if (count(self::$_instance->_tokens) == 0) {
0372                 // require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
0373                 return new Zend_Search_Lucene_Search_Query_Insignificant();
0374             }
0375 
0376 
0377             foreach (self::$_instance->_tokens as $token) {
0378                 try {
0379                     self::$_instance->_currentToken = $token;
0380                     self::$_instance->process($token->type);
0381 
0382                     self::$_instance->_lastToken = $token;
0383                 } catch (Exception $e) {
0384                     if (strpos($e->getMessage(), 'There is no any rule for') !== false) {
0385                         throw new Zend_Search_Lucene_Search_QueryParserException( 'Syntax error at char position ' . $token->position . '.', 0, $e);
0386                     }
0387 
0388                     // require_once 'Zend/Search/Lucene/Exception.php';
0389                     throw new Zend_Search_Lucene_Exception($e->getMessage(), $e->getCode(), $e);
0390                 }
0391             }
0392 
0393             if (count(self::$_instance->_contextStack) != 0) {
0394                 throw new Zend_Search_Lucene_Search_QueryParserException('Syntax Error: mismatched parentheses, every opening must have closing.' );
0395             }
0396 
0397             return self::$_instance->_context->getQuery();
0398         } catch (Zend_Search_Lucene_Search_QueryParserException $e) {
0399             if (self::$_instance->_suppressQueryParsingExceptions) {
0400                 $queryTokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($strQuery, self::$_instance->_encoding);
0401 
0402                 // require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
0403                 $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
0404                 $termsSign = (self::$_instance->_defaultOperator == self::B_AND) ? true /* required term */ :
0405                                                                                    null /* optional term */;
0406 
0407                 // require_once 'Zend/Search/Lucene/Index/Term.php';
0408                 foreach ($queryTokens as $token) {
0409                     $query->addTerm(new Zend_Search_Lucene_Index_Term($token->getTermText()), $termsSign);
0410                 }
0411 
0412 
0413                 return $query;
0414             } else {
0415                 // require_once 'Zend/Search/Lucene/Exception.php';
0416                 throw new Zend_Search_Lucene_Exception($e->getMessage(), $e->getCode(), $e);
0417             }
0418         }
0419     }
0420 
0421     /*********************************************************************
0422      * Actions implementation
0423      *
0424      * Actions affect on recognized lexemes list
0425      *********************************************************************/
0426 
0427     /**
0428      * Add term to a query
0429      */
0430     public function addTermEntry()
0431     {
0432         // require_once 'Zend/Search/Lucene/Search/QueryEntry/Term.php';
0433         $entry = new Zend_Search_Lucene_Search_QueryEntry_Term($this->_currentToken->text, $this->_context->getField());
0434         $this->_context->addEntry($entry);
0435     }
0436 
0437     /**
0438      * Add phrase to a query
0439      */
0440     public function addPhraseEntry()
0441     {
0442         // require_once 'Zend/Search/Lucene/Search/QueryEntry/Phrase.php';
0443         $entry = new Zend_Search_Lucene_Search_QueryEntry_Phrase($this->_currentToken->text, $this->_context->getField());
0444         $this->_context->addEntry($entry);
0445     }
0446 
0447     /**
0448      * Set entry field
0449      */
0450     public function setField()
0451     {
0452         $this->_context->setNextEntryField($this->_currentToken->text);
0453     }
0454 
0455     /**
0456      * Set entry sign
0457      */
0458     public function setSign()
0459     {
0460         $this->_context->setNextEntrySign($this->_currentToken->type);
0461     }
0462 
0463 
0464     /**
0465      * Process fuzzy search/proximity modifier - '~'
0466      */
0467     public function processFuzzyProximityModifier()
0468     {
0469         $this->_context->processFuzzyProximityModifier();
0470     }
0471 
0472     /**
0473      * Process modifier parameter
0474      *
0475      * @throws Zend_Search_Lucene_Exception
0476      */
0477     public function processModifierParameter()
0478     {
0479         if ($this->_lastToken === null) {
0480             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0481             throw new Zend_Search_Lucene_Search_QueryParserException('Lexeme modifier parameter must follow lexeme modifier. Char position 0.' );
0482         }
0483 
0484         switch ($this->_lastToken->type) {
0485             case Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK:
0486                 $this->_context->processFuzzyProximityModifier($this->_currentToken->text);
0487                 break;
0488 
0489             case Zend_Search_Lucene_Search_QueryToken::TT_BOOSTING_MARK:
0490                 $this->_context->boost($this->_currentToken->text);
0491                 break;
0492 
0493             default:
0494                 // It's not a user input exception
0495                 // require_once 'Zend/Search/Lucene/Exception.php';
0496                 throw new Zend_Search_Lucene_Exception('Lexeme modifier parameter must follow lexeme modifier. Char position 0.' );
0497         }
0498     }
0499 
0500 
0501     /**
0502      * Start subquery
0503      */
0504     public function subqueryStart()
0505     {
0506         // require_once 'Zend/Search/Lucene/Search/QueryParserContext.php';
0507 
0508         $this->_contextStack[] = $this->_context;
0509         $this->_context        = new Zend_Search_Lucene_Search_QueryParserContext($this->_encoding, $this->_context->getField());
0510     }
0511 
0512     /**
0513      * End subquery
0514      */
0515     public function subqueryEnd()
0516     {
0517         if (count($this->_contextStack) == 0) {
0518             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0519             throw new Zend_Search_Lucene_Search_QueryParserException('Syntax Error: mismatched parentheses, every opening must have closing. Char position ' . $this->_currentToken->position . '.' );
0520         }
0521 
0522         $query          = $this->_context->getQuery();
0523         $this->_context = array_pop($this->_contextStack);
0524 
0525         // require_once 'Zend/Search/Lucene/Search/QueryEntry/Subquery.php';
0526         $this->_context->addEntry(new Zend_Search_Lucene_Search_QueryEntry_Subquery($query));
0527     }
0528 
0529     /**
0530      * Process logical operator
0531      */
0532     public function logicalOperator()
0533     {
0534         $this->_context->addLogicalOperator($this->_currentToken->type);
0535     }
0536 
0537     /**
0538      * Process first range query term (opened interval)
0539      */
0540     public function openedRQFirstTerm()
0541     {
0542         $this->_rqFirstTerm = $this->_currentToken->text;
0543     }
0544 
0545     /**
0546      * Process last range query term (opened interval)
0547      *
0548      * @throws Zend_Search_Lucene_Search_QueryParserException
0549      */
0550     public function openedRQLastTerm()
0551     {
0552         $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_rqFirstTerm, $this->_encoding);
0553         if (count($tokens) > 1) {
0554             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0555             throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
0556         } else if (count($tokens) == 1) {
0557             // require_once 'Zend/Search/Lucene/Index/Term.php';
0558             $from = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
0559         } else {
0560             $from = null;
0561         }
0562 
0563         $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_currentToken->text, $this->_encoding);
0564         if (count($tokens) > 1) {
0565             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0566             throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
0567         } else if (count($tokens) == 1) {
0568             // require_once 'Zend/Search/Lucene/Index/Term.php';
0569             $to = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
0570         } else {
0571             $to = null;
0572         }
0573 
0574         if ($from === null  &&  $to === null) {
0575             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0576             throw new Zend_Search_Lucene_Search_QueryParserException('At least one range query boundary term must be non-empty term');
0577         }
0578 
0579         // require_once 'Zend/Search/Lucene/Search/Query/Range.php';
0580         $rangeQuery = new Zend_Search_Lucene_Search_Query_Range($from, $to, false);
0581         // require_once 'Zend/Search/Lucene/Search/QueryEntry/Subquery.php';
0582         $entry      = new Zend_Search_Lucene_Search_QueryEntry_Subquery($rangeQuery);
0583         $this->_context->addEntry($entry);
0584     }
0585 
0586     /**
0587      * Process first range query term (closed interval)
0588      */
0589     public function closedRQFirstTerm()
0590     {
0591         $this->_rqFirstTerm = $this->_currentToken->text;
0592     }
0593 
0594     /**
0595      * Process last range query term (closed interval)
0596      *
0597      * @throws Zend_Search_Lucene_Search_QueryParserException
0598      */
0599     public function closedRQLastTerm()
0600     {
0601         $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_rqFirstTerm, $this->_encoding);
0602         if (count($tokens) > 1) {
0603             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0604             throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
0605         } else if (count($tokens) == 1) {
0606             // require_once 'Zend/Search/Lucene/Index/Term.php';
0607             $from = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
0608         } else {
0609             $from = null;
0610         }
0611 
0612         $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_currentToken->text, $this->_encoding);
0613         if (count($tokens) > 1) {
0614             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0615             throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
0616         } else if (count($tokens) == 1) {
0617             // require_once 'Zend/Search/Lucene/Index/Term.php';
0618             $to = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
0619         } else {
0620             $to = null;
0621         }
0622 
0623         if ($from === null  &&  $to === null) {
0624             // require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
0625             throw new Zend_Search_Lucene_Search_QueryParserException('At least one range query boundary term must be non-empty term');
0626         }
0627 
0628         // require_once 'Zend/Search/Lucene/Search/Query/Range.php';
0629         $rangeQuery = new Zend_Search_Lucene_Search_Query_Range($from, $to, true);
0630         // require_once 'Zend/Search/Lucene/Search/QueryEntry/Subquery.php';
0631         $entry      = new Zend_Search_Lucene_Search_QueryEntry_Subquery($rangeQuery);
0632         $this->_context->addEntry($entry);
0633     }
0634 }
0635