Coverage Report - org.jaxen.saxpath.base.XPathReader

Classes in this File Line Coverage Branch Coverage Complexity
XPathReader
98% 
100% 
4.171

 1  
 /*
 2  
  * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/saxpath/base/XPathReader.java,v 1.27 2005/08/09 12:01:49 elharo Exp $
 3  
  * $Revision: 1.27 $
 4  
  * $Date: 2005/08/09 12:01:49 $
 5  
  *
 6  
  * ====================================================================
 7  
  *
 8  
  * Copyright (C) 2000-2002 bob mcwhirter & James Strachan.
 9  
  * All rights reserved.
 10  
  *
 11  
  * Redistribution and use in source and binary forms, with or without
 12  
  * modification, are permitted provided that the following conditions
 13  
  * are met:
 14  
  *
 15  
  * 1. Redistributions of source code must retain the above copyright
 16  
  *    notice, this list of conditions, and the following disclaimer.
 17  
  *
 18  
  * 2. Redistributions in binary form must reproduce the above copyright
 19  
  *    notice, this list of conditions, and the disclaimer that follows
 20  
  *    these conditions in the documentation and/or other materials
 21  
  *    provided with the distribution.
 22  
  *
 23  
  * 3. The name "Jaxen" must not be used to endorse or promote products
 24  
  *    derived from this software without prior written permission.  For
 25  
  *    written permission, please contact license@jaxen.org.
 26  
  *
 27  
  * 4. Products derived from this software may not be called "Jaxen", nor
 28  
  *    may "Jaxen" appear in their name, without prior written permission
 29  
  *    from the Jaxen Project Management (pm@jaxen.org).
 30  
  *
 31  
  * In addition, we request (but do not require) that you include in the
 32  
  * end-user documentation provided with the redistribution and/or in the
 33  
  * software itself an acknowledgement equivalent to the following:
 34  
  *     "This product includes software developed by the
 35  
  *      Jaxen Project <http://www.jaxen.org/>."
 36  
  * Alternatively, the acknowledgment may be graphical using the logos
 37  
  * available at http://www.jaxen.org/
 38  
  *
 39  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED TokenTypes.OR IMPLIED
 40  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 41  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 42  
  * DISCLAIMED.  IN NO EVENT SHALL THE Jaxen AUTHORS TokenTypes.OR THE PROJECT
 43  
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 44  
  * SPECIAL, EXEMPLARY, TokenTypes.OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 45  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS TokenTypes.OR SERVICES; LOSS OF
 46  
  * USE, DATA, TokenTypes.OR PROFITS; TokenTypes.OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 47  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 48  
  * TokenTypes.OR TORT (INCLUDING NEGLIGENCE TokenTypes.OR OTHERWISE) ARISING IN ANY WAY OUT
 49  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 50  
  * SUCH DAMAGE.
 51  
  *
 52  
  * ====================================================================
 53  
  * This software consists of voluntary contributions made by many
 54  
  * individuals on behalf of the Jaxen Project and was originally
 55  
  * created by bob mcwhirter <bob@werken.com> and
 56  
  * James Strachan <jstrachan@apache.org>.  For more information on the
 57  
  * Jaxen Project, please see <http://www.jaxen.org/>.
 58  
  *
 59  
  * $Id: XPathReader.java,v 1.27 2005/08/09 12:01:49 elharo Exp $
 60  
  */
 61  
 
 62  
 
 63  
 package org.jaxen.saxpath.base;
 64  
 
 65  
 import java.util.LinkedList;
 66  
 
 67  
 import org.jaxen.saxpath.Axis;
 68  
 import org.jaxen.saxpath.Operator;
 69  
 import org.jaxen.saxpath.SAXPathException;
 70  
 import org.jaxen.saxpath.XPathHandler;
 71  
 import org.jaxen.saxpath.XPathSyntaxException;
 72  
 import org.jaxen.saxpath.helpers.DefaultXPathHandler;
 73  
 
 74  
 /** Implementation of SAXPath's <code>XPathReader</code> which
 75  
  *  generates callbacks to an <code>XPathHandler</code>.
 76  
  *
 77  
  *  @author bob mcwhirter (bob@werken.com)
 78  
  */
 79  
 public class XPathReader implements org.jaxen.saxpath.XPathReader
 80  
 {
 81  
     private LinkedList tokens;
 82  
     private XPathLexer lexer;
 83  
 
 84  
     private XPathHandler handler;
 85  
     
 86  318
     private static XPathHandler defaultHandler = new DefaultXPathHandler();
 87  
 
 88  
     /**
 89  
      * Create a new <code>XPathReader</code> with a do-nothing
 90  
      * <code>XPathHandler</code>.
 91  
      */
 92  
     public XPathReader()
 93  10896
     {
 94  10896
         setXPathHandler( defaultHandler );
 95  10896
     }
 96  
 
 97  
     public void setXPathHandler(XPathHandler handler)
 98  
     {
 99  21786
         this.handler = handler;
 100  21786
     }
 101  
 
 102  
     public XPathHandler getXPathHandler()
 103  
     {
 104  303654
         return this.handler;
 105  
     }
 106  
 
 107  
     public void parse(String xpath) throws SAXPathException
 108  
     {
 109  11076
         setUpParse( xpath );
 110  
 
 111  11076
         getXPathHandler().startXPath();
 112  
 
 113  11076
         expr();
 114  
 
 115  10818
         getXPathHandler().endXPath();
 116  
 
 117  10818
         if ( LA(1) != TokenTypes.EOF )
 118  
         {
 119  54
             XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
 120  54
             throw ex;
 121  
         }
 122  
 
 123  10764
         lexer  = null;
 124  10764
         tokens = null;
 125  10764
     }
 126  
 
 127  
     void setUpParse(String xpath)
 128  
     {
 129  11136
         this.tokens = new LinkedList();
 130  11136
         this.lexer = new XPathLexer( xpath );
 131  11136
     }
 132  
 
 133  
     private void pathExpr() throws SAXPathException
 134  
     {
 135  23544
         getXPathHandler().startPathExpr();
 136  
 
 137  23544
         switch ( LA(1) )
 138  
         {
 139  
             case TokenTypes.INTEGER:
 140  
             case TokenTypes.DOUBLE:
 141  
             case TokenTypes.LITERAL:
 142  
             {
 143  8502
                 filterExpr();
 144  
 
 145  8502
                 if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH )
 146  
                 {
 147  6
                     XPathSyntaxException ex = createSyntaxException("Node-set expected");
 148  6
                     throw ex;
 149  
                 }
 150  
 
 151  
                 break;
 152  
             }                
 153  
             case TokenTypes.LEFT_PAREN:
 154  
             case TokenTypes.DOLLAR:
 155  
             {
 156  444
                 filterExpr();
 157  
                     
 158  414
                 if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
 159  
                 {
 160  30
                     locationPath( false );
 161  
                 }
 162  
                 break;
 163  
             }
 164  
             case TokenTypes.IDENTIFIER:
 165  
             {
 166  
 
 167  6852
                 if ( ( LA(2) == TokenTypes.LEFT_PAREN
 168  
                      &&
 169  
                        ! isNodeTypeName( LT(1) ) )
 170  
                      ||
 171  
                     ( LA(2) == TokenTypes.COLON
 172  
                       &&
 173  
                       LA(4) == TokenTypes.LEFT_PAREN) ) 
 174  
                 {
 175  4632
                     filterExpr();
 176  
                     
 177  4608
                     if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
 178  
                     {
 179  156
                         locationPath( false );
 180  
                     }
 181  
                 }
 182  
                 else
 183  
                 {
 184  2220
                     locationPath( false );
 185  
                 }
 186  2184
                 break;
 187  
             }
 188  
             case TokenTypes.DOT:
 189  
             case TokenTypes.DOT_DOT:
 190  
             case TokenTypes.STAR:
 191  
             case TokenTypes.AT:
 192  
             {
 193  1638
                 locationPath( false );
 194  1638
                 break;
 195  
             }
 196  
             case TokenTypes.SLASH:
 197  
             case TokenTypes.DOUBLE_SLASH:
 198  
             {
 199  6078
                 locationPath( true );
 200  5928
                 break;
 201  
             }
 202  
             default:
 203  
             {
 204  30
                 XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
 205  30
                 throw ex;
 206  
             }
 207  
         }
 208  
 
 209  23256
         getXPathHandler().endPathExpr();
 210  23256
     }
 211  
 
 212  
     private void numberDouble() throws SAXPathException
 213  
     {
 214  276
         Token token = match( TokenTypes.DOUBLE );
 215  
 
 216  276
         getXPathHandler().number( Double.parseDouble( token.getTokenText() ) );
 217  276
     }
 218  
 
 219  
     private void numberInteger() throws SAXPathException
 220  
     {
 221  4278
         Token token = match( TokenTypes.INTEGER );
 222  
         
 223  4278
         String text = token.getTokenText();
 224  
         try {
 225  4278
             getXPathHandler().number( Integer.parseInt( text ) );
 226  
         }
 227  0
         catch (NumberFormatException ex) {
 228  0
             getXPathHandler().number( Double.parseDouble( text ) );
 229  4278
         }
 230  
         
 231  4278
     }
 232  
 
 233  
     private void literal() throws SAXPathException
 234  
     {
 235  3948
         Token token = match( TokenTypes.LITERAL );
 236  
 
 237  3948
         getXPathHandler().literal( token.getTokenText() );
 238  3948
     }
 239  
 
 240  
     private void functionCall() throws SAXPathException
 241  
     {
 242  4632
         String prefix       = null;
 243  4632
         String functionName = null;
 244  
 
 245  4632
         if ( LA(2) == TokenTypes.COLON )
 246  
         {
 247  6
             prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
 248  6
             match( TokenTypes.COLON );
 249  
         }
 250  
         else
 251  
         {
 252  4626
             prefix = "";
 253  
         }
 254  
 
 255  4632
         functionName = match( TokenTypes.IDENTIFIER ).getTokenText();
 256  
 
 257  4632
         getXPathHandler().startFunction( prefix,
 258  
                                          functionName );
 259  
 
 260  4632
         match ( TokenTypes.LEFT_PAREN );
 261  
 
 262  4632
         arguments();
 263  
 
 264  4608
         match ( TokenTypes.RIGHT_PAREN );
 265  
 
 266  4608
         getXPathHandler().endFunction();
 267  4608
     }
 268  
 
 269  
     private void arguments() throws SAXPathException
 270  
     {
 271  6534
         while ( LA(1) != TokenTypes.RIGHT_PAREN )
 272  
         {
 273  5496
             expr();
 274  
 
 275  5472
             if ( LA(1) == TokenTypes.COMMA )
 276  
             {
 277  1902
                 match( TokenTypes.COMMA );
 278  
             }
 279  
             else
 280  
             {
 281  
                 break;
 282  
             }
 283  
         }
 284  4608
     }
 285  
 
 286  
     private void filterExpr() throws SAXPathException
 287  
     {
 288  
 
 289  13578
         getXPathHandler().startFilterExpr();
 290  
 
 291  13578
         switch ( LA(1) )
 292  
         {
 293  
             case TokenTypes.INTEGER:
 294  
             {
 295  4278
                 numberInteger();
 296  4278
                 break;
 297  
             }
 298  
             case TokenTypes.DOUBLE:
 299  
             {
 300  276
                 numberDouble();
 301  276
                 break;
 302  
             }
 303  
             case TokenTypes.LITERAL:
 304  
             {
 305  3948
                 literal();
 306  3948
                 break;
 307  
             }
 308  
             case TokenTypes.LEFT_PAREN:
 309  
             {
 310  306
                 match( TokenTypes.LEFT_PAREN );
 311  306
                 expr();
 312  306
                 match( TokenTypes.RIGHT_PAREN );
 313  276
                 break;
 314  
             }
 315  
             case TokenTypes.IDENTIFIER:
 316  
             {
 317  4632
                 functionCall();
 318  4608
                 break;
 319  
             }
 320  
             case TokenTypes.DOLLAR:
 321  
             {
 322  138
                 variableReference();
 323  
                 break;
 324  
             }
 325  
         }
 326  
 
 327  13524
         predicates();
 328  
 
 329  13524
         getXPathHandler().endFilterExpr();
 330  13524
     }
 331  
 
 332  
     private void variableReference() throws SAXPathException
 333  
     {
 334  138
         match( TokenTypes.DOLLAR );
 335  
 
 336  138
         String prefix       = null;
 337  138
         String variableName = null;
 338  
 
 339  138
         if ( LA(2) == TokenTypes.COLON )
 340  
         {
 341  6
             prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
 342  6
             match( TokenTypes.COLON );
 343  
         }
 344  
         else
 345  
         {
 346  132
             prefix = "";
 347  
         }
 348  
 
 349  138
         variableName = match( TokenTypes.IDENTIFIER ).getTokenText();
 350  
 
 351  138
         getXPathHandler().variableReference( prefix,
 352  
                                              variableName );
 353  138
     }
 354  
 
 355  
     void locationPath(boolean isAbsolute) throws SAXPathException
 356  
     {
 357  10134
         switch ( LA(1) )
 358  
         {
 359  
             case TokenTypes.SLASH:
 360  
             case TokenTypes.DOUBLE_SLASH:
 361  
             {
 362  6270
                 if ( isAbsolute )
 363  
                 {
 364  6084
                     absoluteLocationPath();
 365  
                 }
 366  
                 else
 367  
                 {
 368  186
                     relativeLocationPath();
 369  
                 }
 370  174
                 break;
 371  
             }
 372  
             case TokenTypes.AT:
 373  
             case TokenTypes.IDENTIFIER:
 374  
             case TokenTypes.DOT:
 375  
             case TokenTypes.DOT_DOT:
 376  
             case TokenTypes.STAR:
 377  
             {
 378  3864
                 relativeLocationPath();
 379  3828
                 break;
 380  
             }
 381  
             default:
 382  
             {
 383  0
                 XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
 384  0
                 throw ex;
 385  
             }
 386  
         }
 387  9936
     }
 388  
 
 389  
     private void absoluteLocationPath() throws SAXPathException
 390  
     {
 391  6084
         getXPathHandler().startAbsoluteLocationPath();
 392  
 
 393  6084
         switch ( LA(1) )
 394  
         {
 395  
             case TokenTypes.SLASH:
 396  
             {
 397  5070
                 match( TokenTypes.SLASH );
 398  
 
 399  5070
                 switch ( LA(1) )
 400  
                 {
 401  
 
 402  
                     case TokenTypes.DOT:
 403  
                     case TokenTypes.DOT_DOT:
 404  
                     case TokenTypes.AT:
 405  
                     case TokenTypes.IDENTIFIER:
 406  
                     case TokenTypes.STAR:
 407  
                     {
 408  4104
                         steps();
 409  
                         break;
 410  
                     }
 411  
                 }
 412  4950
                 break;
 413  
             }
 414  
             case TokenTypes.DOUBLE_SLASH:
 415  
             {
 416  1014
                 getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
 417  1014
                 getXPathHandler().endAllNodeStep();
 418  
 
 419  1014
                 match( TokenTypes.DOUBLE_SLASH );
 420  1014
                 switch ( LA(1) )
 421  
                 {
 422  
                     case TokenTypes.DOT:
 423  
                     case TokenTypes.DOT_DOT:
 424  
                     case TokenTypes.AT:
 425  
                     case TokenTypes.IDENTIFIER:
 426  
                     case TokenTypes.STAR:
 427  
                     {
 428  984
                         steps();
 429  984
                         break;
 430  
                     }
 431  
                     default:
 432  30
                         XPathSyntaxException ex = this.createSyntaxException("Location path cannot end with //");
 433  30
                         throw ex;
 434  
                 }
 435  
                 break;
 436  
             }
 437  
         }
 438  
         
 439  5934
         getXPathHandler().endAbsoluteLocationPath();
 440  5934
     }
 441  
 
 442  
     private void relativeLocationPath() throws SAXPathException
 443  
     {
 444  4050
         getXPathHandler().startRelativeLocationPath();
 445  
 
 446  4050
         switch ( LA(1) )
 447  
         {
 448  
             case TokenTypes.SLASH:
 449  
             {
 450  186
                 match( TokenTypes.SLASH );
 451  186
                 break;
 452  
             }
 453  
             case TokenTypes.DOUBLE_SLASH:
 454  
             {
 455  0
                 getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
 456  0
                 getXPathHandler().endAllNodeStep();
 457  
 
 458  0
                 match( TokenTypes.DOUBLE_SLASH );
 459  
 
 460  
                 break;
 461  
             }
 462  
         }
 463  
 
 464  4050
         steps();
 465  
 
 466  4002
         getXPathHandler().endRelativeLocationPath();
 467  4002
     }
 468  
 
 469  
     private void steps() throws SAXPathException
 470  
     {
 471  9138
         switch ( LA(1) )
 472  
         {
 473  
 
 474  
             case TokenTypes.DOT:
 475  
             case TokenTypes.DOT_DOT:
 476  
             case TokenTypes.AT:
 477  
             case TokenTypes.IDENTIFIER:
 478  
             case TokenTypes.STAR:
 479  
             {
 480  9126
                 step();
 481  9090
                 break;
 482  
             }
 483  
             case TokenTypes.EOF:
 484  
             {
 485  0
                 return;
 486  
             }
 487  
             default:
 488  
             {
 489  12
                 XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', <QName>" );
 490  12
                 throw ex;
 491  
             }
 492  
         }
 493  
 
 494  
         do
 495  
         {
 496  15102
             if ( ( LA(1) == TokenTypes.SLASH)
 497  
                  ||
 498  
                  ( LA(1) == TokenTypes.DOUBLE_SLASH ) )
 499  
             {
 500  6132
                 switch ( LA(1) )
 501  
                 {
 502  
                     case TokenTypes.SLASH:
 503  
                     {
 504  6060
                         match( TokenTypes.SLASH );
 505  6060
                         break;
 506  
                     }
 507  
                     case TokenTypes.DOUBLE_SLASH:
 508  
                     {
 509  72
                         getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
 510  72
                         getXPathHandler().endAllNodeStep();
 511  
 
 512  72
                         match( TokenTypes.DOUBLE_SLASH );
 513  6132
                         break;
 514  
                     }
 515  
                 }
 516  
             }
 517  
             else
 518  
             {
 519  8970
                 return;
 520  
             }
 521  
             
 522  6132
             switch ( LA(1) )
 523  
             {
 524  
                 case TokenTypes.DOT:
 525  
                 case TokenTypes.DOT_DOT:
 526  
                 case TokenTypes.AT:
 527  
                 case TokenTypes.IDENTIFIER:
 528  
                 case TokenTypes.STAR:
 529  
                 {
 530  6108
                     step();
 531  6012
                     break;
 532  
                 }
 533  
                 default:
 534  
                 {
 535  24
                     XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', <QName>" );
 536  24
                     throw ex;
 537  
                 }
 538  
             }
 539  
 
 540  
         } while ( true );
 541  
     }
 542  
 
 543  
     void step() throws SAXPathException
 544  
     {
 545  15282
         int axis = 0;
 546  
 
 547  15282
         switch ( LA(1) )
 548  
         {
 549  
             case TokenTypes.DOT:
 550  
             case TokenTypes.DOT_DOT:
 551  
             {
 552  648
                 abbrStep();
 553  648
                 return;
 554  
             }
 555  
             case TokenTypes.AT:
 556  
             {
 557  1308
                 axis = axisSpecifier();
 558  1308
                 break;
 559  
             }
 560  
             case TokenTypes.IDENTIFIER:
 561  
             {
 562  11916
                 if ( LA(2) == TokenTypes.DOUBLE_COLON )
 563  
                 {
 564  2622
                     axis = axisSpecifier();
 565  
                 }
 566  
                 else
 567  
                 {
 568  9294
                     axis = Axis.CHILD;
 569  
                 }
 570  9294
                 break;
 571  
             }
 572  
             case TokenTypes.STAR:
 573  
             {
 574  1410
                 axis = Axis.CHILD;
 575  
                 break;
 576  
             }
 577  
         }
 578  
 
 579  14622
         nodeTest( axis );
 580  14502
     }
 581  
 
 582  
     private int axisSpecifier() throws SAXPathException
 583  
     {
 584  3930
         int axis = 0;
 585  
 
 586  3930
         switch ( LA(1) )
 587  
         {
 588  
             case TokenTypes.AT:
 589  
             {
 590  1308
                 match( TokenTypes.AT );
 591  1308
                 axis = Axis.ATTRIBUTE;
 592  1308
                 break;
 593  
             }
 594  
             case TokenTypes.IDENTIFIER:
 595  
             {
 596  2622
                 Token token = LT( 1 );
 597  
 
 598  2622
                 axis = Axis.lookup( token.getTokenText() );
 599  
 
 600  2622
                 if ( axis == Axis.INVALID_AXIS )
 601  
                 {
 602  12
                     throwInvalidAxis( token.getTokenText() );
 603  
                 }
 604  
 
 605  2610
                 match( TokenTypes.IDENTIFIER );
 606  2610
                 match( TokenTypes.DOUBLE_COLON );
 607  
 
 608  2610
                 break;
 609  
             }
 610  
         }
 611  
 
 612  3918
         return axis;
 613  
     }
 614  
 
 615  
     private void nodeTest(int axis) throws SAXPathException
 616  
     {
 617  14622
         switch ( LA(1) )
 618  
         {
 619  
             case TokenTypes.IDENTIFIER:
 620  
             {
 621  11976
                 switch ( LA(2) )
 622  
                 {
 623  
                     case TokenTypes.LEFT_PAREN:
 624  
                     {
 625  1206
                         nodeTypeTest( axis );
 626  1194
                         break;
 627  
                     }
 628  
                     default:
 629  
                     {
 630  10770
                         nameTest( axis );
 631  10710
                         break;
 632  
                     }
 633  
                 }
 634  
                 break;
 635  
             }
 636  
             case TokenTypes.STAR:
 637  
             {
 638  2598
                 nameTest( axis );
 639  2598
                 break;
 640  
             }
 641  
             default:
 642  48
                 XPathSyntaxException ex = createSyntaxException("Expected <QName> or *");
 643  48
                 throw ex;
 644  
         }
 645  14502
     }
 646  
 
 647  
     private void nodeTypeTest(int axis) throws SAXPathException
 648  
     {
 649  1206
         Token  nodeTypeToken = match( TokenTypes.IDENTIFIER );
 650  1206
         String nodeType      = nodeTypeToken.getTokenText();
 651  
 
 652  1206
         match( TokenTypes.LEFT_PAREN );
 653  
 
 654  1206
         if ( "processing-instruction".equals( nodeType ) )
 655  
         {
 656  222
             String piName = "";
 657  
 
 658  222
             if ( LA(1) == TokenTypes.LITERAL )
 659  
             {
 660  54
                 piName = match( TokenTypes.LITERAL ).getTokenText();
 661  
             }
 662  
 
 663  222
             match( TokenTypes.RIGHT_PAREN );
 664  
 
 665  222
             getXPathHandler().startProcessingInstructionNodeStep( axis,
 666  
                                                                   piName );
 667  
 
 668  222
             predicates();
 669  
 
 670  222
             getXPathHandler().endProcessingInstructionNodeStep();
 671  
         }
 672  984
         else if ( "node".equals( nodeType ) )
 673  
         {
 674  570
             match( TokenTypes.RIGHT_PAREN );
 675  
 
 676  570
             getXPathHandler().startAllNodeStep( axis );
 677  
 
 678  570
             predicates();
 679  
 
 680  570
             getXPathHandler().endAllNodeStep();
 681  
         }
 682  414
         else if ( "text".equals( nodeType ) )
 683  
         {
 684  246
             match( TokenTypes.RIGHT_PAREN );
 685  
 
 686  246
             getXPathHandler().startTextNodeStep( axis );
 687  
 
 688  246
             predicates();
 689  
 
 690  246
             getXPathHandler().endTextNodeStep();
 691  
         }
 692  168
         else if ( "comment".equals( nodeType ) )
 693  
         {
 694  156
             match( TokenTypes.RIGHT_PAREN );
 695  
 
 696  156
             getXPathHandler().startCommentNodeStep( axis );
 697  
 
 698  156
             predicates();
 699  
 
 700  156
             getXPathHandler().endCommentNodeStep();
 701  
         }
 702  
         else
 703  
         {
 704  12
             XPathSyntaxException ex = createSyntaxException( "Expected node-type" );
 705  12
             throw ex;
 706  
         }
 707  1194
     }
 708  
 
 709  
     private void nameTest(int axis) throws SAXPathException
 710  
     {
 711  13368
         String prefix    = null;
 712  13368
         String localName = null;
 713  
 
 714  13368
         switch ( LA(2) )
 715  
         {
 716  
             case TokenTypes.COLON:
 717  
             {
 718  540
                 switch ( LA(1) )
 719  
                 {
 720  
                     case TokenTypes.IDENTIFIER:
 721  
                     {
 722  534
                         prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
 723  534
                         match( TokenTypes.COLON );
 724  
                         break;
 725  
                     }
 726  
                 }
 727  
                 break;
 728  
             }
 729  
         }
 730  
         
 731  13368
         switch ( LA(1) )
 732  
         {
 733  
             case TokenTypes.IDENTIFIER:
 734  
             {
 735  10758
                 localName = match( TokenTypes.IDENTIFIER ).getTokenText();
 736  10758
                 break;
 737  
             }
 738  
             case TokenTypes.STAR:
 739  
             {
 740  2610
                 match( TokenTypes.STAR );
 741  2610
                 localName = "*";
 742  
                 break;
 743  
             }
 744  
         }
 745  
 
 746  13368
         if ( prefix == null )
 747  
         {
 748  12834
             prefix = "";
 749  
         }
 750  
         
 751  13368
         getXPathHandler().startNameStep( axis,
 752  
                                          prefix,
 753  
                                          localName );
 754  
 
 755  13368
         predicates();
 756  
 
 757  13308
         getXPathHandler().endNameStep();
 758  13308
     }
 759  
 
 760  
     private void abbrStep() throws SAXPathException
 761  
     {
 762  648
         switch ( LA(1) )
 763  
         {
 764  
             case TokenTypes.DOT:
 765  
             {
 766  450
                 match( TokenTypes.DOT );
 767  450
                 getXPathHandler().startAllNodeStep( Axis.SELF );
 768  450
                 predicates();
 769  450
                 getXPathHandler().endAllNodeStep();
 770  450
                 break;
 771  
             }
 772  
             case TokenTypes.DOT_DOT:
 773  
             {
 774  198
                 match( TokenTypes.DOT_DOT );
 775  198
                 getXPathHandler().startAllNodeStep( Axis.PARENT );
 776  198
                 predicates();
 777  198
                 getXPathHandler().endAllNodeStep();
 778  
                 break;
 779  
             }
 780  
         }
 781  648
     }
 782  
 
 783  
     private void predicates() throws SAXPathException
 784  
     {
 785  
         while (true )
 786  
         {
 787  31074
             if ( LA(1) == TokenTypes.LEFT_BRACKET )
 788  
             {
 789  2400
                 predicate();
 790  
             }
 791  
             else
 792  
             {
 793  
                 break;
 794  
             }
 795  
         }
 796  28674
     }
 797  
     
 798  
     void predicate() throws SAXPathException
 799  
     {
 800  2400
         getXPathHandler().startPredicate();
 801  
         
 802  2400
         match( TokenTypes.LEFT_BRACKET );
 803  
         
 804  2400
         predicateExpr();
 805  
 
 806  2394
         match( TokenTypes.RIGHT_BRACKET );
 807  
 
 808  2340
         getXPathHandler().endPredicate();
 809  2340
     }
 810  
 
 811  
     private void predicateExpr() throws SAXPathException
 812  
     {
 813  2400
         expr();
 814  2394
     }
 815  
 
 816  
     private void expr() throws SAXPathException
 817  
     {
 818  19398
         orExpr();
 819  19110
     }
 820  
 
 821  
     private void orExpr() throws SAXPathException
 822  
     {
 823  19428
         getXPathHandler().startOrExpr();
 824  
         
 825  19428
         andExpr();
 826  
 
 827  19140
         boolean create = false;
 828  
 
 829  19140
         switch ( LA(1) )
 830  
         {
 831  
             case TokenTypes.OR:
 832  
             {
 833  30
                 create = true;
 834  30
                 match( TokenTypes.OR );
 835  30
                 orExpr();
 836  
                 break;
 837  
             }
 838  
         }
 839  
 
 840  19140
         getXPathHandler().endOrExpr( create );
 841  19140
     }
 842  
 
 843  
     private void andExpr() throws SAXPathException
 844  
     {
 845  19836
         getXPathHandler().startAndExpr();
 846  
 
 847  19836
         equalityExpr();
 848  
 
 849  19548
         boolean create = false;
 850  
 
 851  19548
         switch ( LA(1) )
 852  
         {
 853  
             case TokenTypes.AND:
 854  
             {
 855  408
                 create = true;
 856  408
                 match( TokenTypes.AND );
 857  408
                 andExpr();
 858  
                 break;
 859  
             }
 860  
         }
 861  
 
 862  19542
         getXPathHandler().endAndExpr( create );
 863  19542
     }
 864  
 
 865  
     private void equalityExpr() throws SAXPathException
 866  
     {
 867  19836
         relationalExpr();
 868  
 
 869  19548
         int la = LA(1);
 870  21486
         while (la == TokenTypes.EQUALS || la == TokenTypes.NOT_EQUALS)
 871  
         {
 872  1938
             switch ( la )
 873  
             {
 874  
                 case TokenTypes.EQUALS:
 875  
                 {
 876  1722
                     match( TokenTypes.EQUALS );
 877  1722
                     getXPathHandler().startEqualityExpr();
 878  1722
                     relationalExpr();
 879  1722
                     getXPathHandler().endEqualityExpr( Operator.EQUALS );
 880  1722
                     break;
 881  
                 }
 882  
                 case TokenTypes.NOT_EQUALS:
 883  
                 {
 884  216
                     match( TokenTypes.NOT_EQUALS );
 885  216
                     getXPathHandler().startEqualityExpr();
 886  216
                     relationalExpr();
 887  216
                     getXPathHandler().endEqualityExpr( Operator.NOT_EQUALS );
 888  
                     break;
 889  
                 }
 890  
             }
 891  1938
             la = LA(1);
 892  
         }
 893  19548
     }
 894  
     
 895  
     private void relationalExpr() throws SAXPathException
 896  
     {
 897  
 
 898  21774
         additiveExpr();
 899  
 
 900  21486
         int la = LA(1);
 901  
         // Very important: TokenTypes.LESS_THAN != Operator.LESS_THAN
 902  
         //                 TokenTypes.GREATER_THAN != Operator.GREATER_THAN
 903  
         //                 TokenTypes.GREATER_THAN_EQUALS != Operator.GREATER_THAN_EQUALS
 904  
         //                 TokenTypes.LESS_THAN_EQUALS != Operator.LESS_THAN_EQUALS
 905  
         while (la == TokenTypes.LESS_THAN_SIGN 
 906  
             || la == TokenTypes.GREATER_THAN_SIGN 
 907  
             || la == TokenTypes.LESS_THAN_OR_EQUALS_SIGN 
 908  22092
             || la == TokenTypes.GREATER_THAN_OR_EQUALS_SIGN ) {
 909  606
             switch ( la )
 910  
             {
 911  
                 case TokenTypes.LESS_THAN_SIGN:
 912  
                 {
 913  150
                     match( TokenTypes.LESS_THAN_SIGN );
 914  150
                     getXPathHandler().startRelationalExpr();
 915  150
                     additiveExpr();
 916  150
                     getXPathHandler().endRelationalExpr( Operator.LESS_THAN );
 917  150
                     break;
 918  
                 }
 919  
                 case TokenTypes.GREATER_THAN_SIGN:
 920  
                 {
 921  204
                     match( TokenTypes.GREATER_THAN_SIGN );
 922  204
                     getXPathHandler().startRelationalExpr();
 923  204
                     additiveExpr();
 924  204
                     getXPathHandler().endRelationalExpr( Operator.GREATER_THAN );
 925  204
                     break;
 926  
                 }
 927  
                 case TokenTypes.GREATER_THAN_OR_EQUALS_SIGN:
 928  
                 {
 929  156
                     match( TokenTypes.GREATER_THAN_OR_EQUALS_SIGN );
 930  156
                     getXPathHandler().startRelationalExpr();
 931  156
                     additiveExpr();
 932  156
                     getXPathHandler().endRelationalExpr( Operator.GREATER_THAN_EQUALS );
 933  156
                     break;
 934  
                 }
 935  
                 case TokenTypes.LESS_THAN_OR_EQUALS_SIGN:
 936  
                 {
 937  96
                     match( TokenTypes.LESS_THAN_OR_EQUALS_SIGN );
 938  96
                     getXPathHandler().startRelationalExpr();
 939  96
                     additiveExpr();
 940  96
                     getXPathHandler().endRelationalExpr( Operator.LESS_THAN_EQUALS );
 941  
                     break;
 942  
                 }
 943  
             }
 944  606
             la = LA(1);
 945  
         }
 946  21486
     } 
 947  
 
 948  
     
 949  
     private void additiveExpr() throws SAXPathException
 950  
     {
 951  22380
         multiplicativeExpr();
 952  
 
 953  22098
         int la = LA(1);
 954  22746
         while (la == TokenTypes.PLUS || la == TokenTypes.MINUS)
 955  
         {
 956  654
             switch ( la )
 957  
             {
 958  
                 case TokenTypes.PLUS:
 959  
                 {
 960  438
                     match( TokenTypes.PLUS );
 961  438
                     getXPathHandler().startAdditiveExpr();
 962  438
                     multiplicativeExpr();
 963  432
                     getXPathHandler().endAdditiveExpr( Operator.ADD );
 964  432
                     break;
 965  
                 }
 966  
                 case TokenTypes.MINUS:
 967  
                 {
 968  216
                     match( TokenTypes.MINUS );
 969  216
                     getXPathHandler().startAdditiveExpr();
 970  216
                     multiplicativeExpr();
 971  216
                     getXPathHandler().endAdditiveExpr( Operator.SUBTRACT );
 972  
                     break;
 973  
                 }
 974  
             }
 975  648
             la = LA(1);
 976  
         }
 977  22092
     }
 978  
 
 979  
     private void multiplicativeExpr() throws SAXPathException
 980  
     {
 981  23034
         unaryExpr();
 982  
 
 983  22746
         int la = LA(1);
 984  23256
         while (la == TokenTypes.STAR || la == TokenTypes.DIV || la == TokenTypes.MOD)
 985  
         {
 986  510
             switch ( la )
 987  
             {
 988  
                 case TokenTypes.STAR:
 989  
                 {
 990  150
                     match( TokenTypes.STAR );
 991  150
                     getXPathHandler().startMultiplicativeExpr();
 992  150
                     unaryExpr();
 993  150
                     getXPathHandler().endMultiplicativeExpr( Operator.MULTIPLY );
 994  150
                     break;
 995  
                 }
 996  
                 case TokenTypes.DIV:
 997  
                 {
 998  294
                     match( TokenTypes.DIV );
 999  294
                     getXPathHandler().startMultiplicativeExpr();
 1000  294
                     unaryExpr();
 1001  294
                     getXPathHandler().endMultiplicativeExpr( Operator.DIV );
 1002  294
                     break;
 1003  
                 }
 1004  
                 case TokenTypes.MOD:
 1005  
                 {
 1006  66
                     match( TokenTypes.MOD );
 1007  66
                     getXPathHandler().startMultiplicativeExpr();
 1008  66
                     unaryExpr();
 1009  66
                     getXPathHandler().endMultiplicativeExpr( Operator.MOD );
 1010  
                     break;
 1011  
                 }
 1012  
             }
 1013  510
             la = LA(1);
 1014  
         }
 1015  
 
 1016  22746
     }
 1017  
 
 1018  
     private void unaryExpr() throws SAXPathException
 1019  
     {
 1020  23784
         switch ( LA(1) )
 1021  
         {
 1022  
             case TokenTypes.MINUS:
 1023  
             {
 1024  240
                 getXPathHandler().startUnaryExpr();
 1025  240
                 match( TokenTypes.MINUS );
 1026  240
                 unaryExpr();
 1027  240
                 getXPathHandler().endUnaryExpr( Operator.NEGATIVE );
 1028  240
                 break;
 1029  
             }
 1030  
             default:
 1031  
             {
 1032  23544
                 unionExpr();
 1033  
                 break;
 1034  
             }
 1035  
         }
 1036  
 
 1037  
         
 1038  23496
     }
 1039  
 
 1040  
     private void unionExpr() throws SAXPathException
 1041  
     {
 1042  23544
         getXPathHandler().startUnionExpr();
 1043  
 
 1044  23544
         pathExpr();
 1045  
 
 1046  23256
         boolean create = false;
 1047  
 
 1048  23256
         switch ( LA(1) )
 1049  
         {
 1050  
             case TokenTypes.PIPE:
 1051  
             {
 1052  120
                 match( TokenTypes.PIPE );
 1053  120
                 create = true;
 1054  120
                 expr();
 1055  
                 break;
 1056  
             }
 1057  
         }
 1058  
 
 1059  23256
         getXPathHandler().endUnionExpr( create );
 1060  23256
     }
 1061  
 
 1062  
     private Token match(int tokenType) throws XPathSyntaxException
 1063  
     {
 1064  72162
         LT(1);
 1065  
 
 1066  72162
         Token token = (Token) tokens.get( 0 );
 1067  
 
 1068  72162
         if ( token.getTokenType() == tokenType )
 1069  
         {
 1070  72078
             tokens.removeFirst();
 1071  72078
             return token;
 1072  
         }
 1073  
 
 1074  
         
 1075  84
         XPathSyntaxException ex = createSyntaxException( "Expected: " + TokenTypes.getTokenText( tokenType ) );
 1076  84
         throw ex;
 1077  
     }
 1078  
 
 1079  
     private int LA(int position)
 1080  
     {
 1081  464820
         return LT(position).getTokenType();
 1082  
     }
 1083  
 
 1084  
     private Token LT(int position)
 1085  
     {
 1086  544710
         if ( tokens.size() <= ( position - 1 ) )
 1087  
         {
 1088  151062
             for ( int i = 0 ; i < position ; ++i )
 1089  
             {
 1090  86538
                 tokens.add( lexer.nextToken() );
 1091  
             }
 1092  
         }
 1093  
 
 1094  544710
         return (Token) tokens.get( position - 1 );
 1095  
     }
 1096  
 
 1097  
     private boolean isNodeTypeName(Token name)
 1098  
     {
 1099  4710
         String text = name.getTokenText();
 1100  
 
 1101  4710
         if ( "node".equals( text )
 1102  
              ||
 1103  
              "comment".equals( text )
 1104  
              ||
 1105  
              "text".equals( text )
 1106  
              ||
 1107  
              "processing-instruction".equals( text ) )
 1108  
         {
 1109  84
             return true;
 1110  
         }
 1111  
 
 1112  4626
         return false;
 1113  
     }
 1114  
 
 1115  
     private XPathSyntaxException createSyntaxException(String message)
 1116  
     {
 1117  300
         String xpath    = this.lexer.getXPath();
 1118  300
         int    position = LT(1).getTokenBegin();
 1119  
 
 1120  300
         return new XPathSyntaxException( xpath,
 1121  
                                          position,
 1122  
                                          message );
 1123  
     }
 1124  
 
 1125  
     private void throwInvalidAxis(String invalidAxis) throws SAXPathException
 1126  
     {
 1127  12
         String xpath    = this.lexer.getXPath();
 1128  12
         int    position = LT(1).getTokenBegin();
 1129  
 
 1130  12
         String message  = "Expected valid axis name instead of [" + invalidAxis + "]";
 1131  
 
 1132  12
         throw new XPathSyntaxException( xpath,
 1133  
                                         position,
 1134  
                                         message );
 1135  
     }
 1136  
 }