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