View Javadoc

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