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 }