View Javadoc

    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      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      {
    94          setXPathHandler( defaultHandler );
    95      }
    96
    97      public void setXPathHandler(XPathHandler handler)
    98      {
    99          this.handler = handler;
    100     }
    101
    102     public XPathHandler getXPathHandler()
    103     {
    104         return this.handler;
    105     }
    106
    107     public void parse(String xpath) throws SAXPathException
    108     {
    109         setUpParse( xpath );
    110
    111         getXPathHandler().startXPath();
    112
    113         expr();
    114
    115         getXPathHandler().endXPath();
    116
    117         if ( LA(1) != TokenTypes.EOF )
    118         {
    119             XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
    120             throw ex;
    121         }
    122
    123         lexer  = null;
    124         tokens = null;
    125     }
    126
    127     void setUpParse(String xpath)
    128     {
    129         this.tokens = new LinkedList();
    130         this.lexer = new XPathLexer( xpath );
    131     }
    132
    133     private void pathExpr() throws SAXPathException
    134     {
    135         getXPathHandler().startPathExpr();
    136
    137         switch ( LA(1) )
    138         {
    139             case TokenTypes.INTEGER:
    140             case TokenTypes.DOUBLE:
    141             case TokenTypes.LITERAL:
    142             {
    143                 filterExpr();
    144
    145                 if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH )
    146                 {
    147                     XPathSyntaxException ex = createSyntaxException("Node-set expected");
    148                     throw ex;
    149                 }
    150
    151                 break;
    152             }
    153             case TokenTypes.LEFT_PAREN:
    154             case TokenTypes.DOLLAR:
    155             {
    156                 filterExpr();
    157
    158                 if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
    159                 {
    160                     locationPath( false );
    161                 }
    162                 break;
    163             }
    164             case TokenTypes.IDENTIFIER:
    165             {
    166
    167                 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                     filterExpr();
    176
    177                     if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
    178                     {
    179                         locationPath( false );
    180                     }
    181                 }
    182                 else
    183                 {
    184                     locationPath( false );
    185                 }
    186                 break;
    187             }
    188             case TokenTypes.DOT:
    189             case TokenTypes.DOT_DOT:
    190             case TokenTypes.STAR:
    191             case TokenTypes.AT:
    192             {
    193                 locationPath( false );
    194                 break;
    195             }
    196             case TokenTypes.SLASH:
    197             case TokenTypes.DOUBLE_SLASH:
    198             {
    199                 locationPath( true );
    200                 break;
    201             }
    202             default:
    203             {
    204                 XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
    205                 throw ex;
    206             }
    207         }
    208
    209         getXPathHandler().endPathExpr();
    210     }
    211
    212     private void numberDouble() throws SAXPathException
    213     {
    214         Token token = match( TokenTypes.DOUBLE );
    215
    216         getXPathHandler().number( Double.parseDouble( token.getTokenText() ) );
    217     }
    218
    219     private void numberInteger() throws SAXPathException
    220     {
    221         Token token = match( TokenTypes.INTEGER );
    222
    223         String text = token.getTokenText();
    224         try {
    225             getXPathHandler().number( Integer.parseInt( text ) );
    226         }
    227         catch (NumberFormatException ex) {
    228             getXPathHandler().number( Double.parseDouble( text ) );
    229         }
    230
    231     }
    232
    233     private void literal() throws SAXPathException
    234     {
    235         Token token = match( TokenTypes.LITERAL );
    236
    237         getXPathHandler().literal( token.getTokenText() );
    238     }
    239
    240     private void functionCall() throws SAXPathException
    241     {
    242         String prefix       = null;
    243         String functionName = null;
    244
    245         if ( LA(2) == TokenTypes.COLON )
    246         {
    247             prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
    248             match( TokenTypes.COLON );
    249         }
    250         else
    251         {
    252             prefix = "";
    253         }
    254
    255         functionName = match( TokenTypes.IDENTIFIER ).getTokenText();
    256
    257         getXPathHandler().startFunction( prefix,
    258                                          functionName );
    259
    260         match ( TokenTypes.LEFT_PAREN );
    261
    262         arguments();
    263
    264         match ( TokenTypes.RIGHT_PAREN );
    265
    266         getXPathHandler().endFunction();
    267     }
    268
    269     private void arguments() throws SAXPathException
    270     {
    271         while ( LA(1) != TokenTypes.RIGHT_PAREN )
    272         {
    273             expr();
    274
    275             if ( LA(1) == TokenTypes.COMMA )
    276             {
    277                 match( TokenTypes.COMMA );
    278             }
    279             else
    280             {
    281                 break;
    282             }
    283         }
    284     }
    285
    286     private void filterExpr() throws SAXPathException
    287     {
    288
    289         getXPathHandler().startFilterExpr();
    290
    291         switch ( LA(1) )
    292         {
    293             case TokenTypes.INTEGER:
    294             {
    295                 numberInteger();
    296                 break;
    297             }
    298             case TokenTypes.DOUBLE:
    299             {
    300                 numberDouble();
    301                 break;
    302             }
    303             case TokenTypes.LITERAL:
    304             {
    305                 literal();
    306                 break;
    307             }
    308             case TokenTypes.LEFT_PAREN:
    309             {
    310                 match( TokenTypes.LEFT_PAREN );
    311                 expr();
    312                 match( TokenTypes.RIGHT_PAREN );
    313                 break;
    314             }
    315             case TokenTypes.IDENTIFIER:
    316             {
    317                 functionCall();
    318                 break;
    319             }
    320             case TokenTypes.DOLLAR:
    321             {
    322                 variableReference();
    323                 break;
    324             }
    325         }
    326
    327         predicates();
    328
    329         getXPathHandler().endFilterExpr();
    330     }
    331
    332     private void variableReference() throws SAXPathException
    333     {
    334         match( TokenTypes.DOLLAR );
    335
    336         String prefix       = null;
    337         String variableName = null;
    338
    339         if ( LA(2) == TokenTypes.COLON )
    340         {
    341             prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
    342             match( TokenTypes.COLON );
    343         }
    344         else
    345         {
    346             prefix = "";
    347         }
    348
    349         variableName = match( TokenTypes.IDENTIFIER ).getTokenText();
    350
    351         getXPathHandler().variableReference( prefix,
    352                                              variableName );
    353     }
    354
    355     void locationPath(boolean isAbsolute) throws SAXPathException
    356     {
    357         switch ( LA(1) )
    358         {
    359             case TokenTypes.SLASH:
    360             case TokenTypes.DOUBLE_SLASH:
    361             {
    362                 if ( isAbsolute )
    363                 {
    364                     absoluteLocationPath();
    365                 }
    366                 else
    367                 {
    368                     relativeLocationPath();
    369                 }
    370                 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                 relativeLocationPath();
    379                 break;
    380             }
    381             default:
    382             {
    383                 XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
    384                 throw ex;
    385             }
    386         }
    387     }
    388
    389     private void absoluteLocationPath() throws SAXPathException
    390     {
    391         getXPathHandler().startAbsoluteLocationPath();
    392
    393         switch ( LA(1) )
    394         {
    395             case TokenTypes.SLASH:
    396             {
    397                 match( TokenTypes.SLASH );
    398
    399                 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                         steps();
    409                         break;
    410                     }
    411                 }
    412                 break;
    413             }
    414             case TokenTypes.DOUBLE_SLASH:
    415             {
    416                 getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
    417                 getXPathHandler().endAllNodeStep();
    418
    419                 match( TokenTypes.DOUBLE_SLASH );
    420                 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                         steps();
    429                         break;
    430                     }
    431                     default:
    432                         XPathSyntaxException ex = this.createSyntaxException("Location path cannot end with //");
    433                         throw ex;
    434                 }
    435                 break;
    436             }
    437         }
    438
    439         getXPathHandler().endAbsoluteLocationPath();
    440     }
    441
    442     private void relativeLocationPath() throws SAXPathException
    443     {
    444         getXPathHandler().startRelativeLocationPath();
    445
    446         switch ( LA(1) )
    447         {
    448             case TokenTypes.SLASH:
    449             {
    450                 match( TokenTypes.SLASH );
    451                 break;
    452             }
    453             case TokenTypes.DOUBLE_SLASH:
    454             {
    455                 getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
    456                 getXPathHandler().endAllNodeStep();
    457
    458                 match( TokenTypes.DOUBLE_SLASH );
    459
    460                 break;
    461             }
    462         }
    463
    464         steps();
    465
    466         getXPathHandler().endRelativeLocationPath();
    467     }
    468
    469     private void steps() throws SAXPathException
    470     {
    471         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                 step();
    481                 break;
    482             }
    483             case TokenTypes.EOF:
    484             {
    485                 return;
    486             }
    487             default:
    488             {
    489                 XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', <QName>" );
    490                 throw ex;
    491             }
    492         }
    493
    494         do
    495         {
    496             if ( ( LA(1) == TokenTypes.SLASH)
    497                  ||
    498                  ( LA(1) == TokenTypes.DOUBLE_SLASH ) )
    499             {
    500                 switch ( LA(1) )
    501                 {
    502                     case TokenTypes.SLASH:
    503                     {
    504                         match( TokenTypes.SLASH );
    505                         break;
    506                     }
    507                     case TokenTypes.DOUBLE_SLASH:
    508                     {
    509                         getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
    510                         getXPathHandler().endAllNodeStep();
    511
    512                         match( TokenTypes.DOUBLE_SLASH );
    513                         break;
    514                     }
    515                 }
    516             }
    517             else
    518             {
    519                 return;
    520             }
    521
    522             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                     step();
    531                     break;
    532                 }
    533                 default:
    534                 {
    535                     XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', <QName>" );
    536                     throw ex;
    537                 }
    538             }
    539
    540         } while ( true );
    541     }
    542
    543     void step() throws SAXPathException
    544     {
    545         int axis = 0;
    546
    547         switch ( LA(1) )
    548         {
    549             case TokenTypes.DOT:
    550             case TokenTypes.DOT_DOT:
    551             {
    552                 abbrStep();
    553                 return;
    554             }
    555             case TokenTypes.AT:
    556             {
    557                 axis = axisSpecifier();
    558                 break;
    559             }
    560             case TokenTypes.IDENTIFIER:
    561             {
    562                 if ( LA(2) == TokenTypes.DOUBLE_COLON )
    563                 {
    564                     axis = axisSpecifier();
    565                 }
    566                 else
    567                 {
    568                     axis = Axis.CHILD;
    569                 }
    570                 break;
    571             }
    572             case TokenTypes.STAR:
    573             {
    574                 axis = Axis.CHILD;
    575                 break;
    576             }
    577         }
    578
    579         nodeTest( axis );
    580     }
    581
    582     private int axisSpecifier() throws SAXPathException
    583     {
    584         int axis = 0;
    585
    586         switch ( LA(1) )
    587         {
    588             case TokenTypes.AT:
    589             {
    590                 match( TokenTypes.AT );
    591                 axis = Axis.ATTRIBUTE;
    592                 break;
    593             }
    594             case TokenTypes.IDENTIFIER:
    595             {
    596                 Token token = LT( 1 );
    597
    598                 axis = Axis.lookup( token.getTokenText() );
    599
    600                 if ( axis == Axis.INVALID_AXIS )
    601                 {
    602                     throwInvalidAxis( token.getTokenText() );
    603                 }
    604
    605                 match( TokenTypes.IDENTIFIER );
    606                 match( TokenTypes.DOUBLE_COLON );
    607
    608                 break;
    609             }
    610         }
    611
    612         return axis;
    613     }
    614
    615     private void nodeTest(int axis) throws SAXPathException
    616     {
    617         switch ( LA(1) )
    618         {
    619             case TokenTypes.IDENTIFIER:
    620             {
    621                 switch ( LA(2) )
    622                 {
    623                     case TokenTypes.LEFT_PAREN:
    624                     {
    625                         nodeTypeTest( axis );
    626                         break;
    627                     }
    628                     default:
    629                     {
    630                         nameTest( axis );
    631                         break;
    632                     }
    633                 }
    634                 break;
    635             }
    636             case TokenTypes.STAR:
    637             {
    638                 nameTest( axis );
    639                 break;
    640             }
    641             default:
    642                 XPathSyntaxException ex = createSyntaxException("Expected <QName> or *");
    643                 throw ex;
    644         }
    645     }
    646
    647     private void nodeTypeTest(int axis) throws SAXPathException
    648     {
    649         Token  nodeTypeToken = match( TokenTypes.IDENTIFIER );
    650         String nodeType      = nodeTypeToken.getTokenText();
    651
    652         match( TokenTypes.LEFT_PAREN );
    653
    654         if ( "processing-instruction".equals( nodeType ) )
    655         {
    656             String piName = "";
    657
    658             if ( LA(1) == TokenTypes.LITERAL )
    659             {
    660                 piName = match( TokenTypes.LITERAL ).getTokenText();
    661             }
    662
    663             match( TokenTypes.RIGHT_PAREN );
    664
    665             getXPathHandler().startProcessingInstructionNodeStep( axis,
    666                                                                   piName );
    667
    668             predicates();
    669
    670             getXPathHandler().endProcessingInstructionNodeStep();
    671         }
    672         else if ( "node".equals( nodeType ) )
    673         {
    674             match( TokenTypes.RIGHT_PAREN );
    675
    676             getXPathHandler().startAllNodeStep( axis );
    677
    678             predicates();
    679
    680             getXPathHandler().endAllNodeStep();
    681         }
    682         else if ( "text".equals( nodeType ) )
    683         {
    684             match( TokenTypes.RIGHT_PAREN );
    685
    686             getXPathHandler().startTextNodeStep( axis );
    687
    688             predicates();
    689
    690             getXPathHandler().endTextNodeStep();
    691         }
    692         else if ( "comment".equals( nodeType ) )
    693         {
    694             match( TokenTypes.RIGHT_PAREN );
    695
    696             getXPathHandler().startCommentNodeStep( axis );
    697
    698             predicates();
    699
    700             getXPathHandler().endCommentNodeStep();
    701         }
    702         else
    703         {
    704             XPathSyntaxException ex = createSyntaxException( "Expected node-type" );
    705             throw ex;
    706         }
    707     }
    708
    709     private void nameTest(int axis) throws SAXPathException
    710     {
    711         String prefix    = null;
    712         String localName = null;
    713
    714         switch ( LA(2) )
    715         {
    716             case TokenTypes.COLON:
    717             {
    718                 switch ( LA(1) )
    719                 {
    720                     case TokenTypes.IDENTIFIER:
    721                     {
    722                         prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
    723                         match( TokenTypes.COLON );
    724                         break;
    725                     }
    726                 }
    727                 break;
    728             }
    729         }
    730
    731         switch ( LA(1) )
    732         {
    733             case TokenTypes.IDENTIFIER:
    734             {
    735                 localName = match( TokenTypes.IDENTIFIER ).getTokenText();
    736                 break;
    737             }
    738             case TokenTypes.STAR:
    739             {
    740                 match( TokenTypes.STAR );
    741                 localName = "*";
    742                 break;
    743             }
    744         }
    745
    746         if ( prefix == null )
    747         {
    748             prefix = "";
    749         }
    750
    751         getXPathHandler().startNameStep( axis,
    752                                          prefix,
    753                                          localName );
    754
    755         predicates();
    756
    757         getXPathHandler().endNameStep();
    758     }
    759
    760     private void abbrStep() throws SAXPathException
    761     {
    762         switch ( LA(1) )
    763         {
    764             case TokenTypes.DOT:
    765             {
    766                 match( TokenTypes.DOT );
    767                 getXPathHandler().startAllNodeStep( Axis.SELF );
    768                 predicates();
    769                 getXPathHandler().endAllNodeStep();
    770                 break;
    771             }
    772             case TokenTypes.DOT_DOT:
    773             {
    774                 match( TokenTypes.DOT_DOT );
    775                 getXPathHandler().startAllNodeStep( Axis.PARENT );
    776                 predicates();
    777                 getXPathHandler().endAllNodeStep();
    778                 break;
    779             }
    780         }
    781     }
    782
    783     private void predicates() throws SAXPathException
    784     {
    785         while (true )
    786         {
    787             if ( LA(1) == TokenTypes.LEFT_BRACKET )
    788             {
    789                 predicate();
    790             }
    791             else
    792             {
    793                 break;
    794             }
    795         }
    796     }
    797
    798     void predicate() throws SAXPathException
    799     {
    800         getXPathHandler().startPredicate();
    801
    802         match( TokenTypes.LEFT_BRACKET );
    803
    804         predicateExpr();
    805
    806         match( TokenTypes.RIGHT_BRACKET );
    807
    808         getXPathHandler().endPredicate();
    809     }
    810
    811     private void predicateExpr() throws SAXPathException
    812     {
    813         expr();
    814     }
    815
    816     private void expr() throws SAXPathException
    817     {
    818         orExpr();
    819     }
    820
    821     private void orExpr() throws SAXPathException
    822     {
    823         getXPathHandler().startOrExpr();
    824
    825         andExpr();
    826
    827         boolean create = false;
    828
    829         switch ( LA(1) )
    830         {
    831             case TokenTypes.OR:
    832             {
    833                 create = true;
    834                 match( TokenTypes.OR );
    835                 orExpr();
    836                 break;
    837             }
    838         }
    839
    840         getXPathHandler().endOrExpr( create );
    841     }
    842
    843     private void andExpr() throws SAXPathException
    844     {
    845         getXPathHandler().startAndExpr();
    846
    847         equalityExpr();
    848
    849         boolean create = false;
    850
    851         switch ( LA(1) )
    852         {
    853             case TokenTypes.AND:
    854             {
    855                 create = true;
    856                 match( TokenTypes.AND );
    857                 andExpr();
    858                 break;
    859             }
    860         }
    861
    862         getXPathHandler().endAndExpr( create );
    863     }
    864
    865     private void equalityExpr() throws SAXPathException
    866     {
    867         relationalExpr();
    868
    869         int la = LA(1);
    870         while (la == TokenTypes.EQUALS || la == TokenTypes.NOT_EQUALS)
    871         {
    872             switch ( la )
    873             {
    874                 case TokenTypes.EQUALS:
    875                 {
    876                     match( TokenTypes.EQUALS );
    877                     getXPathHandler().startEqualityExpr();
    878                     relationalExpr();
    879                     getXPathHandler().endEqualityExpr( Operator.EQUALS );
    880                     break;
    881                 }
    882                 case TokenTypes.NOT_EQUALS:
    883                 {
    884                     match( TokenTypes.NOT_EQUALS );
    885                     getXPathHandler().startEqualityExpr();
    886                     relationalExpr();
    887                     getXPathHandler().endEqualityExpr( Operator.NOT_EQUALS );
    888                     break;
    889                 }
    890             }
    891             la = LA(1);
    892         }
    893     }
    894
    895     private void relationalExpr() throws SAXPathException
    896     {
    897
    898         additiveExpr();
    899
    900         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             || la == TokenTypes.GREATER_THAN_OR_EQUALS_SIGN ) {
    909             switch ( la )
    910             {
    911                 case TokenTypes.LESS_THAN_SIGN:
    912                 {
    913                     match( TokenTypes.LESS_THAN_SIGN );
    914                     getXPathHandler().startRelationalExpr();
    915                     additiveExpr();
    916                     getXPathHandler().endRelationalExpr( Operator.LESS_THAN );
    917                     break;
    918                 }
    919                 case TokenTypes.GREATER_THAN_SIGN:
    920                 {
    921                     match( TokenTypes.GREATER_THAN_SIGN );
    922                     getXPathHandler().startRelationalExpr();
    923                     additiveExpr();
    924                     getXPathHandler().endRelationalExpr( Operator.GREATER_THAN );
    925                     break;
    926                 }
    927                 case TokenTypes.GREATER_THAN_OR_EQUALS_SIGN:
    928                 {
    929                     match( TokenTypes.GREATER_THAN_OR_EQUALS_SIGN );
    930                     getXPathHandler().startRelationalExpr();
    931                     additiveExpr();
    932                     getXPathHandler().endRelationalExpr( Operator.GREATER_THAN_EQUALS );
    933                     break;
    934                 }
    935                 case TokenTypes.LESS_THAN_OR_EQUALS_SIGN:
    936                 {
    937                     match( TokenTypes.LESS_THAN_OR_EQUALS_SIGN );
    938                     getXPathHandler().startRelationalExpr();
    939                     additiveExpr();
    940                     getXPathHandler().endRelationalExpr( Operator.LESS_THAN_EQUALS );
    941                     break;
    942                 }
    943             }
    944             la = LA(1);
    945         }
    946     }
    947
    948
    949     private void additiveExpr() throws SAXPathException
    950     {
    951         multiplicativeExpr();
    952
    953         int la = LA(1);
    954         while (la == TokenTypes.PLUS || la == TokenTypes.MINUS)
    955         {
    956             switch ( la )
    957             {
    958                 case TokenTypes.PLUS:
    959                 {
    960                     match( TokenTypes.PLUS );
    961                     getXPathHandler().startAdditiveExpr();
    962                     multiplicativeExpr();
    963                     getXPathHandler().endAdditiveExpr( Operator.ADD );
    964                     break;
    965                 }
    966                 case TokenTypes.MINUS:
    967                 {
    968                     match( TokenTypes.MINUS );
    969                     getXPathHandler().startAdditiveExpr();
    970                     multiplicativeExpr();
    971                     getXPathHandler().endAdditiveExpr( Operator.SUBTRACT );
    972                     break;
    973                 }
    974             }
    975             la = LA(1);
    976         }
    977     }
    978
    979     private void multiplicativeExpr() throws SAXPathException
    980     {
    981         unaryExpr();
    982
    983         int la = LA(1);
    984         while (la == TokenTypes.STAR || la == TokenTypes.DIV || la == TokenTypes.MOD)
    985         {
    986             switch ( la )
    987             {
    988                 case TokenTypes.STAR:
    989                 {
    990                     match( TokenTypes.STAR );
    991                     getXPathHandler().startMultiplicativeExpr();
    992                     unaryExpr();
    993                     getXPathHandler().endMultiplicativeExpr( Operator.MULTIPLY );
    994                     break;
    995                 }
    996                 case TokenTypes.DIV:
    997                 {
    998                     match( TokenTypes.DIV );
    999                     getXPathHandler().startMultiplicativeExpr();
    1000                     unaryExpr();
    1001                     getXPathHandler().endMultiplicativeExpr( Operator.DIV );
    1002                     break;
    1003                 }
    1004                 case TokenTypes.MOD:
    1005                 {
    1006                     match( TokenTypes.MOD );
    1007                     getXPathHandler().startMultiplicativeExpr();
    1008                     unaryExpr();
    1009                     getXPathHandler().endMultiplicativeExpr( Operator.MOD );
    1010                     break;
    1011                 }
    1012             }
    1013             la = LA(1);
    1014         }
    1015
    1016     }
    1017
    1018     private void unaryExpr() throws SAXPathException
    1019     {
    1020         switch ( LA(1) )
    1021         {
    1022             case TokenTypes.MINUS:
    1023             {
    1024                 getXPathHandler().startUnaryExpr();
    1025                 match( TokenTypes.MINUS );
    1026                 unaryExpr();
    1027                 getXPathHandler().endUnaryExpr( Operator.NEGATIVE );
    1028                 break;
    1029             }
    1030             default:
    1031             {
    1032                 unionExpr();
    1033                 break;
    1034             }
    1035         }
    1036
    1037
    1038     }
    1039
    1040     private void unionExpr() throws SAXPathException
    1041     {
    1042         getXPathHandler().startUnionExpr();
    1043
    1044         pathExpr();
    1045
    1046         boolean create = false;
    1047
    1048         switch ( LA(1) )
    1049         {
    1050             case TokenTypes.PIPE:
    1051             {
    1052                 match( TokenTypes.PIPE );
    1053                 create = true;
    1054                 expr();
    1055                 break;
    1056             }
    1057         }
    1058
    1059         getXPathHandler().endUnionExpr( create );
    1060     }
    1061
    1062     private Token match(int tokenType) throws XPathSyntaxException
    1063     {
    1064         LT(1);
    1065
    1066         Token token = (Token) tokens.get( 0 );
    1067
    1068         if ( token.getTokenType() == tokenType )
    1069         {
    1070             tokens.removeFirst();
    1071             return token;
    1072         }
    1073
    1074
    1075         XPathSyntaxException ex = createSyntaxException( "Expected: " + TokenTypes.getTokenText( tokenType ) );
    1076         throw ex;
    1077     }
    1078
    1079     private int LA(int position)
    1080     {
    1081         return LT(position).getTokenType();
    1082     }
    1083
    1084     private Token LT(int position)
    1085     {
    1086         if ( tokens.size() <= ( position - 1 ) )
    1087         {
    1088             for ( int i = 0 ; i < position ; ++i )
    1089             {
    1090                 tokens.add( lexer.nextToken() );
    1091             }
    1092         }
    1093
    1094         return (Token) tokens.get( position - 1 );
    1095     }
    1096
    1097     private boolean isNodeTypeName(Token name)
    1098     {
    1099         String text = name.getTokenText();
    1100
    1101         if ( "node".equals( text )
    1102              ||
    1103              "comment".equals( text )
    1104              ||
    1105              "text".equals( text )
    1106              ||
    1107              "processing-instruction".equals( text ) )
    1108         {
    1109             return true;
    1110         }
    1111
    1112         return false;
    1113     }
    1114
    1115     private XPathSyntaxException createSyntaxException(String message)
    1116     {
    1117         String xpath    = this.lexer.getXPath();
    1118         int    position = LT(1).getTokenBegin();
    1119
    1120         return new XPathSyntaxException( xpath,
    1121                                          position,
    1122                                          message );
    1123     }
    1124
    1125     private void throwInvalidAxis(String invalidAxis) throws SAXPathException
    1126     {
    1127         String xpath    = this.lexer.getXPath();
    1128         int    position = LT(1).getTokenBegin();
    1129
    1130         String message  = "Expected valid axis name instead of [" + invalidAxis + "]";
    1131
    1132         throw new XPathSyntaxException( xpath,
    1133                                         position,
    1134                                         message );
    1135     }
    1136 }