View Javadoc

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