Coverage Report - org.jaxen.saxpath.base.XPathReader
 
Classes in this File Line Coverage Branch Coverage Complexity
XPathReader
99%
416/422
100%
51/51
4.282
 
 1  
 /*
 2  
  * $Header$
 3  
  * $Revision: 1334 $
 4  
  * $Date: 2008-11-29 09:58:48 -0800 (Sat, 29 Nov 2008) $
 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: XPathReader.java 1334 2008-11-29 17:58:48Z elharo $
 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  299
     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  15446
     {
 81  15446
         setXPathHandler( defaultHandler );
 82  15446
     }
 83  
 
 84  
     public void setXPathHandler(XPathHandler handler)
 85  
     {
 86  30887
         this.handler = handler;
 87  30887
     }
 88  
 
 89  
     public XPathHandler getXPathHandler()
 90  
     {
 91  472468
         return this.handler;
 92  
     }
 93  
 
 94  
     public void parse(String xpath) throws SAXPathException
 95  
     {
 96  15646
         setUpParse( xpath );
 97  
 
 98  15646
         getXPathHandler().startXPath();
 99  
 
 100  15646
         expr();
 101  
 
 102  15411
         getXPathHandler().endXPath();
 103  
 
 104  15411
         if ( LA(1) != TokenTypes.EOF )
 105  
         {
 106  45
             XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
 107  45
             throw ex;
 108  
         }
 109  
 
 110  15366
         lexer  = null;
 111  15366
         tokens = null;
 112  15366
     }
 113  
 
 114  
     void setUpParse(String xpath)
 115  
     {
 116  15646
         this.tokens = new ArrayList();
 117  15646
         this.lexer = new XPathLexer( xpath );
 118  15646
     }
 119  
 
 120  
     private void pathExpr() throws SAXPathException
 121  
     {
 122  37563
         getXPathHandler().startPathExpr();
 123  
 
 124  37563
         switch ( LA(1) )
 125  
         {
 126  
             case TokenTypes.DOUBLE:
 127  
             case TokenTypes.LITERAL:
 128  
             {
 129  13442
                 filterExpr();
 130  
 
 131  13442
                 if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH )
 132  
                 {
 133  5
                     XPathSyntaxException ex = createSyntaxException("Node-set expected");
 134  5
                     throw ex;
 135  
                 }
 136  
 
 137  
                 break;
 138  
             }                
 139  
             case TokenTypes.LEFT_PAREN:
 140  
             case TokenTypes.DOLLAR:
 141  
             {
 142  3740
                 filterExpr();
 143  
                     
 144  3715
                 if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
 145  
                 {
 146  35
                     locationPath( false );
 147  
                 }
 148  
                 break;
 149  
             }
 150  
             case TokenTypes.IDENTIFIER:
 151  
             {
 152  
 
 153  10972
                 if ( ( LA(2) == TokenTypes.LEFT_PAREN
 154  
                      &&
 155  
                        ! isNodeTypeName( LT(1) ) )
 156  
                      ||
 157  
                     ( LA(2) == TokenTypes.COLON
 158  
                       &&
 159  
                       LA(4) == TokenTypes.LEFT_PAREN) ) 
 160  
                 {
 161  6634
                     filterExpr();
 162  
                     
 163  6614
                     if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
 164  
                     {
 165  250
                         locationPath( false );
 166  
                     }
 167  
                 }
 168  
                 else
 169  
                 {
 170  4338
                     locationPath( false );
 171  
                 }
 172  4308
                 break;
 173  
             }
 174  
             case TokenTypes.DOT:
 175  
             case TokenTypes.DOT_DOT:
 176  
             case TokenTypes.STAR:
 177  
             case TokenTypes.AT:
 178  
             {
 179  1393
                 locationPath( false );
 180  1393
                 break;
 181  
             }
 182  
             case TokenTypes.SLASH:
 183  
             case TokenTypes.DOUBLE_SLASH:
 184  
             {
 185  7971
                 locationPath( true );
 186  7846
                 break;
 187  
             }
 188  
             default:
 189  
             {
 190  45
                 XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
 191  45
                 throw ex;
 192  
             }
 193  
         }
 194  
 
 195  37303
         getXPathHandler().endPathExpr();
 196  37303
     }
 197  
 
 198  
     private void literal() throws SAXPathException
 199  
     {
 200  5549
         Token token = match( TokenTypes.LITERAL );
 201  
 
 202  5549
         getXPathHandler().literal( token.getTokenText() );
 203  5549
     }
 204  
 
 205  
     private void functionCall() throws SAXPathException
 206  
     {
 207  6634
         String prefix       = null;
 208  6634
         String functionName = null;
 209  
 
 210  6634
         if ( LA(2) == TokenTypes.COLON )
 211  
         {
 212  10
             prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
 213  10
             match( TokenTypes.COLON );
 214  
         }
 215  
         else
 216  
         {
 217  6624
             prefix = "";
 218  
         }
 219  
 
 220  6634
         functionName = match( TokenTypes.IDENTIFIER ).getTokenText();
 221  
 
 222  6634
         getXPathHandler().startFunction( prefix,
 223  
                                          functionName );
 224  
 
 225  6634
         match ( TokenTypes.LEFT_PAREN );
 226  
 
 227  6634
         arguments();
 228  
 
 229  6614
         match ( TokenTypes.RIGHT_PAREN );
 230  
 
 231  6614
         getXPathHandler().endFunction();
 232  6614
     }
 233  
 
 234  
     private void arguments() throws SAXPathException
 235  
     {
 236  9207
         while ( LA(1) != TokenTypes.RIGHT_PAREN )
 237  
         {
 238  7591
             expr();
 239  
 
 240  7571
             if ( LA(1) == TokenTypes.COMMA )
 241  
             {
 242  2573
                 match( TokenTypes.COMMA );
 243  
             }
 244  
             else
 245  
             {
 246  
                 break;
 247  
             }
 248  
         }
 249  6614
     }
 250  
 
 251  
     private void filterExpr() throws SAXPathException
 252  
     {
 253  
 
 254  23816
         getXPathHandler().startFilterExpr();
 255  
 
 256  23816
         switch ( LA(1) )
 257  
         {
 258  
             case TokenTypes.DOUBLE:
 259  
             {
 260  7893
                 Token token = match( TokenTypes.DOUBLE );
 261  
                 
 262  7893
                 getXPathHandler().number( Double.parseDouble( token.getTokenText() ) );
 263  7893
                 break;
 264  
             }
 265  
             case TokenTypes.LITERAL:
 266  
             {
 267  5549
                 literal();
 268  5549
                 break;
 269  
             }
 270  
             case TokenTypes.LEFT_PAREN:
 271  
             {
 272  3535
                 match( TokenTypes.LEFT_PAREN );
 273  3535
                 expr();
 274  3535
                 match( TokenTypes.RIGHT_PAREN );
 275  3510
                 break;
 276  
             }
 277  
             case TokenTypes.IDENTIFIER:
 278  
             {
 279  6634
                 functionCall();
 280  6614
                 break;
 281  
             }
 282  
             case TokenTypes.DOLLAR:
 283  
             {
 284  205
                 variableReference();
 285  
                 break;
 286  
             }
 287  
         }
 288  
 
 289  23771
         predicates();
 290  
 
 291  23771
         getXPathHandler().endFilterExpr();
 292  23771
     }
 293  
 
 294  
     private void variableReference() throws SAXPathException
 295  
     {
 296  205
         match( TokenTypes.DOLLAR );
 297  
 
 298  205
         String prefix       = null;
 299  205
         String variableName = null;
 300  
 
 301  205
         if ( LA(2) == TokenTypes.COLON )
 302  
         {
 303  5
             prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
 304  5
             match( TokenTypes.COLON );
 305  
         }
 306  
         else
 307  
         {
 308  200
             prefix = "";
 309  
         }
 310  
 
 311  205
         variableName = match( TokenTypes.IDENTIFIER ).getTokenText();
 312  
 
 313  205
         getXPathHandler().variableReference( prefix,
 314  
                                              variableName );
 315  205
     }
 316  
 
 317  
     void locationPath(boolean isAbsolute) throws SAXPathException
 318  
     {
 319  13987
         switch ( LA(1) )
 320  
         {
 321  
             case TokenTypes.SLASH:
 322  
             case TokenTypes.DOUBLE_SLASH:
 323  
             {
 324  8256
                 if ( isAbsolute )
 325  
                 {
 326  7971
                     absoluteLocationPath();
 327  
                 }
 328  
                 else
 329  
                 {
 330  285
                     relativeLocationPath();
 331  
                 }
 332  275
                 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  5731
                 relativeLocationPath();
 341  5701
                 break;
 342  
             }
 343  
             default:
 344  
             {
 345  0
                 XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
 346  0
                 throw ex;
 347  
             }
 348  
         }
 349  13822
     }
 350  
 
 351  
     private void absoluteLocationPath() throws SAXPathException
 352  
     {
 353  7971
         getXPathHandler().startAbsoluteLocationPath();
 354  
 
 355  7971
         switch ( LA(1) )
 356  
         {
 357  
             case TokenTypes.SLASH:
 358  
             {
 359  7033
                 match( TokenTypes.SLASH );
 360  
 
 361  7033
                 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  6183
                         steps();
 371  
                         break;
 372  
                     }
 373  
                 }
 374  6933
                 break;
 375  
             }
 376  
             case TokenTypes.DOUBLE_SLASH:
 377  
             {
 378  938
                 getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
 379  938
                 getXPathHandler().endAllNodeStep();
 380  
 
 381  938
                 match( TokenTypes.DOUBLE_SLASH );
 382  938
                 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  913
                         steps();
 391  913
                         break;
 392  
                     }
 393  
                     default:
 394  25
                         XPathSyntaxException ex = this.createSyntaxException("Location path cannot end with //");
 395  25
                         throw ex;
 396  
                 }
 397  
                 break;
 398  
             }
 399  
         }
 400  
         
 401  7846
         getXPathHandler().endAbsoluteLocationPath();
 402  7846
     }
 403  
 
 404  
     private void relativeLocationPath() throws SAXPathException
 405  
     {
 406  6016
         getXPathHandler().startRelativeLocationPath();
 407  
 
 408  6016
         switch ( LA(1) )
 409  
         {
 410  
             case TokenTypes.SLASH:
 411  
             {
 412  285
                 match( TokenTypes.SLASH );
 413  285
                 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  6016
         steps();
 427  
 
 428  5976
         getXPathHandler().endRelativeLocationPath();
 429  5976
     }
 430  
 
 431  
     private void steps() throws SAXPathException
 432  
     {
 433  13112
         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  13102
                 step();
 443  13072
                 break;
 444  
             }
 445  
             case TokenTypes.EOF:
 446  
             {
 447  0
                 return;
 448  
             }
 449  
             default:
 450  
             {
 451  10
                 XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', <QName>" );
 452  10
                 throw ex;
 453  
             }
 454  
         }
 455  
 
 456  
         do
 457  
         {
 458  22252
             if ( ( LA(1) == TokenTypes.SLASH)
 459  
                  ||
 460  
                  ( LA(1) == TokenTypes.DOUBLE_SLASH ) )
 461  
             {
 462  9280
                 switch ( LA(1) )
 463  
                 {
 464  
                     case TokenTypes.SLASH:
 465  
                     {
 466  9220
                         match( TokenTypes.SLASH );
 467  9220
                         break;
 468  
                     }
 469  
                     case TokenTypes.DOUBLE_SLASH:
 470  
                     {
 471  60
                         getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
 472  60
                         getXPathHandler().endAllNodeStep();
 473  
 
 474  60
                         match( TokenTypes.DOUBLE_SLASH );
 475  9280
                         break;
 476  
                     }
 477  
                 }
 478  
             }
 479  
             else
 480  
             {
 481  12972
                 return;
 482  
             }
 483  
             
 484  9280
             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  9260
                     step();
 493  9180
                     break;
 494  
                 }
 495  
                 default:
 496  
                 {
 497  20
                     XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', <QName>" );
 498  20
                     throw ex;
 499  
                 }
 500  
             }
 501  
 
 502  
         } while ( true );
 503  
     }
 504  
 
 505  
     void step() throws SAXPathException
 506  
     {
 507  22362
         int axis = 0;
 508  
 
 509  22362
         switch ( LA(1) )
 510  
         {
 511  
             case TokenTypes.DOT:
 512  
             case TokenTypes.DOT_DOT:
 513  
             {
 514  555
                 abbrStep();
 515  555
                 return;
 516  
             }
 517  
             case TokenTypes.AT:
 518  
             {
 519  1095
                 axis = axisSpecifier();
 520  1095
                 break;
 521  
             }
 522  
             case TokenTypes.IDENTIFIER:
 523  
             {
 524  19511
                 if ( LA(2) == TokenTypes.DOUBLE_COLON )
 525  
                 {
 526  11565
                     axis = axisSpecifier();
 527  
                 }
 528  
                 else
 529  
                 {
 530  7946
                     axis = Axis.CHILD;
 531  
                 }
 532  7946
                 break;
 533  
             }
 534  
             case TokenTypes.STAR:
 535  
             {
 536  1201
                 axis = Axis.CHILD;
 537  
                 break;
 538  
             }
 539  
         }
 540  
 
 541  21797
         nodeTest( axis );
 542  21697
     }
 543  
 
 544  
     private int axisSpecifier() throws SAXPathException
 545  
     {
 546  12660
         int axis = 0;
 547  
 
 548  12660
         switch ( LA(1) )
 549  
         {
 550  
             case TokenTypes.AT:
 551  
             {
 552  1095
                 match( TokenTypes.AT );
 553  1095
                 axis = Axis.ATTRIBUTE;
 554  1095
                 break;
 555  
             }
 556  
             case TokenTypes.IDENTIFIER:
 557  
             {
 558  11565
                 Token token = LT( 1 );
 559  
 
 560  11565
                 axis = Axis.lookup( token.getTokenText() );
 561  
 
 562  11565
                 if ( axis == Axis.INVALID_AXIS )
 563  
                 {
 564  10
                     throwInvalidAxis( token.getTokenText() );
 565  
                 }
 566  
 
 567  11555
                 match( TokenTypes.IDENTIFIER );
 568  11555
                 match( TokenTypes.DOUBLE_COLON );
 569  
 
 570  11555
                 break;
 571  
             }
 572  
         }
 573  
 
 574  12650
         return axis;
 575  
     }
 576  
 
 577  
     private void nodeTest(int axis) throws SAXPathException
 578  
     {
 579  21797
         switch ( LA(1) )
 580  
         {
 581  
             case TokenTypes.IDENTIFIER:
 582  
             {
 583  17986
                 switch ( LA(2) )
 584  
                 {
 585  
                     case TokenTypes.LEFT_PAREN:
 586  
                     {
 587  2688
                         nodeTypeTest( axis );
 588  2678
                         break;
 589  
                     }
 590  
                     default:
 591  
                     {
 592  15298
                         nameTest( axis );
 593  15248
                         break;
 594  
                     }
 595  
                 }
 596  
                 break;
 597  
             }
 598  
             case TokenTypes.STAR:
 599  
             {
 600  3771
                 nameTest( axis );
 601  3771
                 break;
 602  
             }
 603  
             default:
 604  40
                 XPathSyntaxException ex = createSyntaxException("Expected <QName> or *");
 605  40
                 throw ex;
 606  
         }
 607  21697
     }
 608  
 
 609  
     private void nodeTypeTest(int axis) throws SAXPathException
 610  
     {
 611  2688
         Token  nodeTypeToken = match( TokenTypes.IDENTIFIER );
 612  2688
         String nodeType      = nodeTypeToken.getTokenText();
 613  
 
 614  2688
         match( TokenTypes.LEFT_PAREN );
 615  
 
 616  2688
         if ( "processing-instruction".equals( nodeType ) )
 617  
         {
 618  358
             String piName = "";
 619  
 
 620  358
             if ( LA(1) == TokenTypes.LITERAL )
 621  
             {
 622  90
                 piName = match( TokenTypes.LITERAL ).getTokenText();
 623  
             }
 624  
 
 625  358
             match( TokenTypes.RIGHT_PAREN );
 626  
 
 627  358
             getXPathHandler().startProcessingInstructionNodeStep( axis,
 628  
                                                                   piName );
 629  
 
 630  358
             predicates();
 631  
 
 632  358
             getXPathHandler().endProcessingInstructionNodeStep();
 633  358
         }
 634  2330
         else if ( "node".equals( nodeType ) )
 635  
         {
 636  1705
             match( TokenTypes.RIGHT_PAREN );
 637  
 
 638  1705
             getXPathHandler().startAllNodeStep( axis );
 639  
 
 640  1705
             predicates();
 641  
 
 642  1705
             getXPathHandler().endAllNodeStep();
 643  
         }
 644  625
         else if ( "text".equals( nodeType ) )
 645  
         {
 646  390
             match( TokenTypes.RIGHT_PAREN );
 647  
 
 648  390
             getXPathHandler().startTextNodeStep( axis );
 649  
 
 650  390
             predicates();
 651  
 
 652  390
             getXPathHandler().endTextNodeStep();
 653  
         }
 654  235
         else if ( "comment".equals( nodeType ) )
 655  
         {
 656  225
             match( TokenTypes.RIGHT_PAREN );
 657  
 
 658  225
             getXPathHandler().startCommentNodeStep( axis );
 659  
 
 660  225
             predicates();
 661  
 
 662  225
             getXPathHandler().endCommentNodeStep();
 663  
         }
 664  
         else
 665  
         {
 666  10
             XPathSyntaxException ex = createSyntaxException( "Expected node-type" );
 667  10
             throw ex;
 668  
         }
 669  2678
     }
 670  
 
 671  
     private void nameTest(int axis) throws SAXPathException
 672  
     {
 673  19069
         String prefix    = null;
 674  19069
         String localName = null;
 675  
 
 676  19069
         switch ( LA(2) )
 677  
         {
 678  
             case TokenTypes.COLON:
 679  
             {
 680  840
                 switch ( LA(1) )
 681  
                 {
 682  
                     case TokenTypes.IDENTIFIER:
 683  
                     {
 684  835
                         prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
 685  835
                         match( TokenTypes.COLON );
 686  
                         break;
 687  
                     }
 688  
                 }
 689  
                 break;
 690  
             }
 691  
         }
 692  
         
 693  19069
         switch ( LA(1) )
 694  
         {
 695  
             case TokenTypes.IDENTIFIER:
 696  
             {
 697  15288
                 localName = match( TokenTypes.IDENTIFIER ).getTokenText();
 698  15288
                 break;
 699  
             }
 700  
             case TokenTypes.STAR:
 701  
             {
 702  3781
                 match( TokenTypes.STAR );
 703  3781
                 localName = "*";
 704  
                 break;
 705  
             }
 706  
         }
 707  
 
 708  19069
         if ( prefix == null )
 709  
         {
 710  18234
             prefix = "";
 711  
         }
 712  
         
 713  19069
         getXPathHandler().startNameStep( axis,
 714  
                                          prefix,
 715  
                                          localName );
 716  
 
 717  19069
         predicates();
 718  
 
 719  19019
         getXPathHandler().endNameStep();
 720  19019
     }
 721  
 
 722  
     private void abbrStep() throws SAXPathException
 723  
     {
 724  555
         switch ( LA(1) )
 725  
         {
 726  
             case TokenTypes.DOT:
 727  
             {
 728  390
                 match( TokenTypes.DOT );
 729  390
                 getXPathHandler().startAllNodeStep( Axis.SELF );
 730  390
                 predicates();
 731  390
                 getXPathHandler().endAllNodeStep();
 732  390
                 break;
 733  
             }
 734  
             case TokenTypes.DOT_DOT:
 735  
             {
 736  165
                 match( TokenTypes.DOT_DOT );
 737  165
                 getXPathHandler().startAllNodeStep( Axis.PARENT );
 738  165
                 predicates();
 739  165
                 getXPathHandler().endAllNodeStep();
 740  
                 break;
 741  
             }
 742  
         }
 743  555
     }
 744  
 
 745  
     private void predicates() throws SAXPathException
 746  
     {
 747  
         while (true )
 748  
         {
 749  49466
             if ( LA(1) == TokenTypes.LEFT_BRACKET )
 750  
             {
 751  3443
                 predicate();
 752  
             }
 753  
             else
 754  
             {
 755  
                 break;
 756  
             }
 757  
         }
 758  46023
     }
 759  
     
 760  
     void predicate() throws SAXPathException
 761  
     {
 762  3443
         getXPathHandler().startPredicate();
 763  
         
 764  3443
         match( TokenTypes.LEFT_BRACKET );
 765  
         
 766  3443
         predicateExpr();
 767  
 
 768  3438
         match( TokenTypes.RIGHT_BRACKET );
 769  
 
 770  3393
         getXPathHandler().endPredicate();
 771  3393
     }
 772  
 
 773  
     private void predicateExpr() throws SAXPathException
 774  
     {
 775  3443
         expr();
 776  3438
     }
 777  
 
 778  
     private void expr() throws SAXPathException
 779  
     {
 780  30315
         orExpr();
 781  30055
     }
 782  
 
 783  
     private void orExpr() throws SAXPathException
 784  
     {
 785  30363
         getXPathHandler().startOrExpr();
 786  
         
 787  30363
         andExpr();
 788  
 
 789  30103
         boolean create = false;
 790  
 
 791  30103
         switch ( LA(1) )
 792  
         {
 793  
             case TokenTypes.OR:
 794  
             {
 795  48
                 create = true;
 796  48
                 match( TokenTypes.OR );
 797  48
                 orExpr();
 798  
                 break;
 799  
             }
 800  
         }
 801  
 
 802  30103
         getXPathHandler().endOrExpr( create );
 803  30103
     }
 804  
 
 805  
     private void andExpr() throws SAXPathException
 806  
     {
 807  30998
         getXPathHandler().startAndExpr();
 808  
 
 809  30998
         equalityExpr();
 810  
 
 811  30738
         boolean create = false;
 812  
 
 813  30738
         switch ( LA(1) )
 814  
         {
 815  
             case TokenTypes.AND:
 816  
             {
 817  635
                 create = true;
 818  635
                 match( TokenTypes.AND );
 819  635
                 andExpr();
 820  
                 break;
 821  
             }
 822  
         }
 823  
 
 824  30733
         getXPathHandler().endAndExpr( create );
 825  30733
     }
 826  
 
 827  
     private void equalityExpr() throws SAXPathException
 828  
     {
 829  30998
         relationalExpr();
 830  
 
 831  30738
         int la = LA(1);
 832  33943
         while (la == TokenTypes.EQUALS || la == TokenTypes.NOT_EQUALS)
 833  
         {
 834  3205
             switch ( la )
 835  
             {
 836  
                 case TokenTypes.EQUALS:
 837  
                 {
 838  2845
                     match( TokenTypes.EQUALS );
 839  2845
                     getXPathHandler().startEqualityExpr();
 840  2845
                     relationalExpr();
 841  2845
                     getXPathHandler().endEqualityExpr( Operator.EQUALS );
 842  2845
                     break;
 843  
                 }
 844  
                 case TokenTypes.NOT_EQUALS:
 845  
                 {
 846  360
                     match( TokenTypes.NOT_EQUALS );
 847  360
                     getXPathHandler().startEqualityExpr();
 848  360
                     relationalExpr();
 849  360
                     getXPathHandler().endEqualityExpr( Operator.NOT_EQUALS );
 850  
                     break;
 851  
                 }
 852  
             }
 853  3205
             la = LA(1);
 854  
         }
 855  30738
     }
 856  
     
 857  
     private void relationalExpr() throws SAXPathException
 858  
     {
 859  
 
 860  34203
         additiveExpr();
 861  
 
 862  33943
         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  
         while (la == TokenTypes.LESS_THAN_SIGN 
 868  
             || la == TokenTypes.GREATER_THAN_SIGN 
 869  
             || la == TokenTypes.LESS_THAN_OR_EQUALS_SIGN 
 870  35093
             || la == TokenTypes.GREATER_THAN_OR_EQUALS_SIGN ) {
 871  1150
             switch ( la )
 872  
             {
 873  
                 case TokenTypes.LESS_THAN_SIGN:
 874  
                 {
 875  305
                     match( TokenTypes.LESS_THAN_SIGN );
 876  305
                     getXPathHandler().startRelationalExpr();
 877  305
                     additiveExpr();
 878  305
                     getXPathHandler().endRelationalExpr( Operator.LESS_THAN );
 879  305
                     break;
 880  
                 }
 881  
                 case TokenTypes.GREATER_THAN_SIGN:
 882  
                 {
 883  315
                     match( TokenTypes.GREATER_THAN_SIGN );
 884  315
                     getXPathHandler().startRelationalExpr();
 885  315
                     additiveExpr();
 886  315
                     getXPathHandler().endRelationalExpr( Operator.GREATER_THAN );
 887  315
                     break;
 888  
                 }
 889  
                 case TokenTypes.GREATER_THAN_OR_EQUALS_SIGN:
 890  
                 {
 891  290
                     match( TokenTypes.GREATER_THAN_OR_EQUALS_SIGN );
 892  290
                     getXPathHandler().startRelationalExpr();
 893  290
                     additiveExpr();
 894  290
                     getXPathHandler().endRelationalExpr( Operator.GREATER_THAN_EQUALS );
 895  290
                     break;
 896  
                 }
 897  
                 case TokenTypes.LESS_THAN_OR_EQUALS_SIGN:
 898  
                 {
 899  240
                     match( TokenTypes.LESS_THAN_OR_EQUALS_SIGN );
 900  240
                     getXPathHandler().startRelationalExpr();
 901  240
                     additiveExpr();
 902  240
                     getXPathHandler().endRelationalExpr( Operator.LESS_THAN_EQUALS );
 903  
                     break;
 904  
                 }
 905  
             }
 906  1150
             la = LA(1);
 907  
         }
 908  33943
     } 
 909  
 
 910  
     
 911  
     private void additiveExpr() throws SAXPathException
 912  
     {
 913  35353
         multiplicativeExpr();
 914  
 
 915  35098
         int la = LA(1);
 916  36358
         while (la == TokenTypes.PLUS || la == TokenTypes.MINUS)
 917  
         {
 918  1265
             switch ( la )
 919  
             {
 920  
                 case TokenTypes.PLUS:
 921  
                 {
 922  745
                     match( TokenTypes.PLUS );
 923  745
                     getXPathHandler().startAdditiveExpr();
 924  745
                     multiplicativeExpr();
 925  740
                     getXPathHandler().endAdditiveExpr( Operator.ADD );
 926  740
                     break;
 927  
                 }
 928  
                 case TokenTypes.MINUS:
 929  
                 {
 930  520
                     match( TokenTypes.MINUS );
 931  520
                     getXPathHandler().startAdditiveExpr();
 932  520
                     multiplicativeExpr();
 933  520
                     getXPathHandler().endAdditiveExpr( Operator.SUBTRACT );
 934  
                     break;
 935  
                 }
 936  
             }
 937  1260
             la = LA(1);
 938  
         }
 939  35093
     }
 940  
 
 941  
     private void multiplicativeExpr() throws SAXPathException
 942  
     {
 943  36618
         unaryExpr();
 944  
        
 945  36358
         int la = LA(1);
 946  37303
         while (la == TokenTypes.STAR_OPERATOR || la == TokenTypes.DIV || la == TokenTypes.MOD)
 947  
         {
 948  945
             switch ( la )
 949  
             {
 950  
                 case TokenTypes.STAR:
 951  
                 case TokenTypes.STAR_OPERATOR:
 952  61
                 {
 953  305
                     match( TokenTypes.STAR_OPERATOR );
 954  305
                     getXPathHandler().startMultiplicativeExpr();
 955  305
                     unaryExpr();
 956  305
                     getXPathHandler().endMultiplicativeExpr( Operator.MULTIPLY );
 957  244
                     break;
 958  
                 }
 959  
                 case TokenTypes.DIV:
 960  93
                 {
 961  465
                     match( TokenTypes.DIV );
 962  465
                     getXPathHandler().startMultiplicativeExpr();
 963  465
                     unaryExpr();
 964  465
                     getXPathHandler().endMultiplicativeExpr( Operator.DIV );
 965  372
                     break;
 966  
                 }
 967  
                 case TokenTypes.MOD:
 968  35
                 {
 969  175
                     match( TokenTypes.MOD );
 970  175
                     getXPathHandler().startMultiplicativeExpr();
 971  175
                     unaryExpr();
 972  140
                     getXPathHandler().endMultiplicativeExpr( Operator.MOD );
 973  
                     break;
 974  
                 }
 975  189
             }
 976  756
             la = LA(1);
 977  
         }
 978  7246
 
 979  29112
     }
 980  
 
 981  
     private void unaryExpr() throws SAXPathException
 982  7555
     {
 983  30348
         switch ( LA(1) )
 984  
         {
 985  
             case TokenTypes.MINUS:
 986  68
             {
 987  340
                 getXPathHandler().startUnaryExpr();
 988  340
                 match( TokenTypes.MINUS );
 989  340
                 unaryExpr();
 990  340
                 getXPathHandler().endUnaryExpr( Operator.NEGATIVE );
 991  272
                 break;
 992  
             }
 993  
             default:
 994  7487
             {
 995  30076
                 unionExpr();
 996  
                 break;
 997  
             }
 998  
         }
 999  
 
 1000  7503
         
 1001  30140
     }
 1002  
 
 1003  
     private void unionExpr() throws SAXPathException
 1004  7487
     {
 1005  30076
         getXPathHandler().startUnionExpr();
 1006  7487
 
 1007  30076
         pathExpr();
 1008  7435
 
 1009  29868
         boolean create = false;
 1010  7435
 
 1011  29868
         switch ( LA(1) )
 1012  
         {
 1013  
             case TokenTypes.PIPE:
 1014  20
             {
 1015  100
                 match( TokenTypes.PIPE );
 1016  100
                 create = true;
 1017  80
                 expr();
 1018  
                 break;
 1019  
             }
 1020  
         }
 1021  7435
 
 1022  37303
         getXPathHandler().endUnionExpr( create );
 1023  29868
     }
 1024  
 
 1025  
     private Token match(int tokenType) throws XPathSyntaxException
 1026  25767
     {
 1027  103388
         LT(1);
 1028  25767
 
 1029  103388
         Token token = (Token) tokens.get( 0 );
 1030  25767
 
 1031  103388
         if ( token.getTokenType() == tokenType )
 1032  25753
         {
 1033  129085
             tokens.remove(0);
 1034  103332
             return token;
 1035  
         }
 1036  
 
 1037  14
         
 1038  70
         XPathSyntaxException ex = createSyntaxException( "Expected: " + TokenTypes.getTokenText( tokenType ) );
 1039  56
         throw ex;
 1040  
     }
 1041  
 
 1042  
     private int LA(int position)
 1043  145193
     {
 1044  583124
         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  174684
     {
 1051  701440
         if ( tokens.size() <= ( position - 1 ) )
 1052  51518
         {
 1053  206744
             for ( int i = 0 ; i < position ; ++i )
 1054  29771
             {
 1055  119468
                 tokens.add( lexer.nextToken() );
 1056  
             }
 1057  
         }
 1058  174684
 
 1059  701440
         return (Token) tokens.get( position - 1 );
 1060  
     }
 1061  
 
 1062  
     private boolean isNodeTypeName(Token name)
 1063  1337
     {
 1064  5380
         String text = name.getTokenText();
 1065  1337
 
 1066  5380
         if ( "node".equals( text )
 1067  
              ||
 1068  
              "comment".equals( text )
 1069  
              ||
 1070  
              "text".equals( text )
 1071  
              ||
 1072  
              "processing-instruction".equals( text ) )
 1073  17
         {
 1074  76
             return true;
 1075  
         }
 1076  1320
 
 1077  5304
         return false;
 1078  
     }
 1079  
 
 1080  
     private XPathSyntaxException createSyntaxException(String message)
 1081  54
     {
 1082  270
         String xpath    = this.lexer.getXPath();
 1083  216
         int    position = LT(1).getTokenBegin();
 1084  54
 
 1085  216
         return new XPathSyntaxException( xpath,
 1086  
                                          position,
 1087  
                                          message );
 1088  
     }
 1089  
 
 1090  
     private void throwInvalidAxis(String invalidAxis) throws SAXPathException
 1091  2
     {
 1092  10
         String xpath    = this.lexer.getXPath();
 1093  8
         int    position = LT(1).getTokenBegin();
 1094  2
 
 1095  8
         String message  = "Expected valid axis name instead of [" + invalidAxis + "]";
 1096  2
 
 1097  8
         throw new XPathSyntaxException( xpath,
 1098  
                                         position,
 1099  
                                         message );
 1100  
     }
 1101  
 }