KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > impl > xpath > XPath


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 2000-2002 The Apache Software Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package com.sun.org.apache.xerces.internal.impl.xpath;
59
60 import java.util.Vector JavaDoc;
61
62 import com.sun.org.apache.xerces.internal.util.SymbolTable;
63 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
64 import com.sun.org.apache.xerces.internal.util.XMLChar;
65 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
66 import com.sun.org.apache.xerces.internal.xni.QName;
67
68 /**
69  * Bare minimum XPath parser.
70  *
71  * @author Andy Clark, IBM
72  * @version $Id: XPath.java,v 1.13 2004/02/09 22:50:01 kohsuke Exp $
73  */

74 public class XPath {
75
76     //
77
// Constants
78
//
79

80     private static final boolean DEBUG_ALL = false;
81
82     private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false;
83
84     private static final boolean DEBUG_ANY = DEBUG_XPATH_PARSE;
85
86     //
87
// Data
88
//
89

90     /** Expression. */
91     protected String JavaDoc fExpression;
92
93     /** Symbol table. */
94     protected SymbolTable fSymbolTable;
95
96     /** Location paths. */
97     protected LocationPath[] fLocationPaths;
98
99     //
100
// Constructors
101
//
102

103     /** Constructs an XPath object from the specified expression. */
104     public XPath(String JavaDoc xpath, SymbolTable symbolTable,
105                  NamespaceContext context)
106         throws XPathException {
107         fExpression = xpath;
108         fSymbolTable = symbolTable;
109         parseExpression(context);
110     } // <init>(String,SymbolTable,NamespaceContext)
111

112     //
113
// Public methods
114
//
115

116     /**
117      * Returns a representation of all location paths for this XPath.
118      * XPath = locationPath ( '|' locationPath)
119      */

120     public LocationPath[] getLocationPaths() {
121         LocationPath[] ret=new LocationPath[fLocationPaths.length];
122         for (int i=0;i<fLocationPaths.length;i++){
123             ret[i]=(LocationPath)fLocationPaths[i].clone();
124         }
125         return ret;
126     } // getLocationPath(LocationPath)
127

128     /** Returns a representation of the first location path for this XPath. */
129     public LocationPath getLocationPath() {
130         return (LocationPath)fLocationPaths[0].clone();
131     } // getLocationPath(LocationPath)
132

133     //
134
// Object methods
135
//
136

137     /** Returns a string representation of this object. */
138     public String JavaDoc toString() {
139         StringBuffer JavaDoc buf=new StringBuffer JavaDoc();
140         for (int i=0;i<fLocationPaths.length;i++){
141             if (i>0){
142                 buf.append("|");
143             }
144             buf.append(fLocationPaths[i].toString());
145         }
146         return buf.toString();
147     } // toString():String
148

149     //
150
// Private methods
151
//
152

153     /**
154      * Used by the {@link #parseExpression(NamespaceContext)} method
155      * to verify the assumption.
156      *
157      * If <tt>b</tt> is false, this method throws XPathException
158      * to report the error.
159      */

160     private static void check( boolean b ) throws XPathException {
161         if(!b) throw new XPathException("c-general-xpath");
162     }
163     
164     /**
165      * Used by the {@link #parseExpression(NamespaceContext)} method
166      * to build a {@link LocationPath} object from the accumulated
167      * {@link Step}s.
168      */

169     private LocationPath buildLocationPath( Vector JavaDoc stepsVector ) throws XPathException {
170         int size = stepsVector.size();
171         check(size!=0);
172         Step[] steps = new Step[size];
173         stepsVector.copyInto(steps);
174         stepsVector.removeAllElements();
175         
176         return new LocationPath(steps);
177     }
178     
179     /**
180      * This method is implemented by using the XPathExprScanner and
181      * examining the list of tokens that it returns.
182      */

183     private void parseExpression(final NamespaceContext context)
184         throws XPathException {
185
186         // tokens
187
final XPath.Tokens xtokens = new XPath.Tokens(fSymbolTable);
188
189         // scanner
190
XPath.Scanner scanner = new XPath.Scanner(fSymbolTable) {
191             protected void addToken(XPath.Tokens tokens, int token)
192                 throws XPathException {
193                 if (
194                     token == XPath.Tokens.EXPRTOKEN_ATSIGN ||
195                     token == XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME ||
196                     token == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH ||
197                     token == XPath.Tokens.EXPRTOKEN_PERIOD ||
198                     token == XPath.Tokens.EXPRTOKEN_NAMETEST_ANY ||
199                     token == XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE ||
200                     token == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH ||
201                     token == XPath.Tokens.EXPRTOKEN_OPERATOR_UNION
202                     //
203
) {
204                     super.addToken(tokens, token);
205                     return;
206                 }
207                 throw new XPathException("c-general-xpath");
208             }
209         };
210
211         int length = fExpression.length();
212         
213         boolean success = scanner.scanExpr(fSymbolTable,
214                                            xtokens, fExpression, 0, length);
215         if(!success)
216             throw new XPathException("c-general-xpath");
217         
218         //fTokens.dumpTokens();
219
Vector JavaDoc stepsVector = new Vector JavaDoc();
220         Vector JavaDoc locationPathsVector= new Vector JavaDoc();
221         
222         // true when the next token should be 'Step' (as defined in
223
// the production rule [3] of XML Schema P1 section 3.11.6
224
// if false, we are expecting either '|' or '/'.
225
//
226
// this is to make sure we can detect a token list like
227
// 'abc' '/' '/' 'def' 'ghi'
228
boolean expectingStep = true;
229
230         while(xtokens.hasMore()) {
231             final int token = xtokens.nextToken();
232
233             switch (token) {
234                 case XPath.Tokens.EXPRTOKEN_OPERATOR_UNION :{
235                     check(!expectingStep);
236                     locationPathsVector.addElement(buildLocationPath(stepsVector));
237                     expectingStep=true;
238                     break;
239                 }
240
241                 case XPath.Tokens.EXPRTOKEN_ATSIGN: {
242                     check(expectingStep);
243                     Step step = new Step(
244                             new Axis(Axis.ATTRIBUTE),
245                             parseNodeTest(xtokens.nextToken(),xtokens,context));
246                     stepsVector.addElement(step);
247                     expectingStep=false;
248                     break;
249                 }
250                 case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
251                 case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
252                 case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: {
253                     check(expectingStep);
254                     Step step = new Step(
255                             new Axis(Axis.CHILD),
256                             parseNodeTest(token,xtokens,context));
257                     stepsVector.addElement(step);
258                     expectingStep=false;
259                     break;
260                 }
261
262                 case XPath.Tokens.EXPRTOKEN_PERIOD: {
263                     check(expectingStep);
264                     expectingStep=false;
265
266                     // unless this is the first step in this location path,
267
// there's really no reason to keep them in LocationPath.
268
// This amounts to shorten "a/././b/./c" to "a/b/c".
269
// Also, the matcher fails to work correctly if XPath
270
// has those redundant dots.
271
if (stepsVector.size()==0) {
272                         // build step
273
Axis axis = new Axis(Axis.SELF);
274                         NodeTest nodeTest = new NodeTest(NodeTest.NODE);
275                         Step step = new Step(axis, nodeTest);
276                         stepsVector.addElement(step);
277                         
278                         if( xtokens.hasMore()
279                          && xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH){
280                             // consume '//'
281
xtokens.nextToken();
282                             
283                             // build step
284
axis = new Axis(Axis.DESCENDANT);
285                             nodeTest = new NodeTest(NodeTest.NODE);
286                             step = new Step(axis, nodeTest);
287                             stepsVector.addElement(step);
288                             expectingStep=true;
289                         }
290                     }
291                     break;
292                 }
293
294                 case XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH:{
295                     // this cannot appear in arbitrary position.
296
// it is only allowed right after '.' when
297
// '.' is the first token of a location path.
298
throw new XPathException("c-general-xpath");
299                 }
300                 case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: {
301                     check(!expectingStep);
302                     expectingStep=true;
303                     break;
304                 }
305                 default:
306                     // we should have covered all the tokens that we can possibly see.
307
throw new InternalError JavaDoc();
308             }
309         }
310         
311         check(!expectingStep);
312
313         locationPathsVector.addElement(buildLocationPath(stepsVector));
314
315         // save location path
316
fLocationPaths=new LocationPath[locationPathsVector.size()];
317         locationPathsVector.copyInto(fLocationPaths);
318
319
320         if (DEBUG_XPATH_PARSE) {
321             System.out.println(">>> "+fLocationPaths);
322         }
323
324     } // parseExpression(SymbolTable,NamespaceContext)
325

326     /**
327      * Used by {@link #parseExpression} to parse a node test
328      * from the token list.
329      */

330     private NodeTest parseNodeTest( int typeToken, Tokens xtokens, NamespaceContext context )
331         throws XPathException {
332         switch(typeToken) {
333         case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
334             return new NodeTest(NodeTest.WILDCARD);
335             
336         case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
337         case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME:
338             // consume QName token
339
String JavaDoc prefix = xtokens.nextTokenAsString();
340             String JavaDoc uri = null;
341             if (context != null && prefix != XMLSymbols.EMPTY_STRING) {
342                 uri = context.getURI(prefix);
343             }
344             if (prefix != XMLSymbols.EMPTY_STRING && context != null && uri == null) {
345                 throw new XPathException("c-general-xpath-ns");
346             }
347     
348             if (typeToken==XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE)
349                 return new NodeTest(prefix,uri);
350     
351             String JavaDoc localpart = xtokens.nextTokenAsString();
352             String JavaDoc rawname = prefix != XMLSymbols.EMPTY_STRING
353             ? fSymbolTable.addSymbol(prefix+':'+localpart)
354             : localpart;
355     
356             return new NodeTest(new QName(prefix, localpart, rawname, uri));
357         
358         default:
359             // assertion error
360
throw new InternalError JavaDoc();
361         }
362     }
363     
364     
365     //
366
// Classes
367
//
368

369     // location path information
370

371     /**
372      * A location path representation for an XPath expression.
373      *
374      * @author Andy Clark, IBM
375      */

376     public static class LocationPath
377         implements Cloneable JavaDoc {
378
379         //
380
// Data
381
//
382

383         /** List of steps. */
384         public Step[] steps;
385
386         //
387
// Constructors
388
//
389

390         /** Creates a location path from a series of steps. */
391         public LocationPath(Step[] steps) {
392             this.steps = steps;
393         } // <init>(Step[])
394

395         /** Copy constructor. */
396         protected LocationPath(LocationPath path) {
397             steps = new Step[path.steps.length];
398             for (int i = 0; i < steps.length; i++) {
399                 steps[i] = (Step)path.steps[i].clone();
400             }
401         } // <init>(LocationPath)
402

403         //
404
// Object methods
405
//
406

407         /** Returns a string representation of this object. */
408         public String JavaDoc toString() {
409             StringBuffer JavaDoc str = new StringBuffer JavaDoc();
410             for (int i = 0; i < steps.length; i++) {
411                 if (i > 0 && (steps[i-1].axis.type!=Axis.DESCENDANT
412                     && steps[i].axis.type!=Axis.DESCENDANT) ){
413                     str.append('/');
414                 }
415                 str.append(steps[i].toString());
416             }
417             // DEBUG: This code is just for debugging and should *not*
418
// be left in because it will mess up hashcodes of
419
// serialized versions of this object. -Ac
420
if (false) {
421                 str.append('[');
422                 String JavaDoc s = super.toString();
423                 str.append(s.substring(s.indexOf('@')));
424                 str.append(']');
425             }
426             return str.toString();
427         } // toString():String
428

429         /** Returns a clone of this object. */
430         public Object JavaDoc clone() {
431             return new LocationPath(this);
432         } // clone():Object
433

434     } // class locationPath
435

436     /**
437      * A location path step comprised of an axis and node test.
438      *
439      * @author Andy Clark, IBM
440      */

441     public static class Step
442         implements Cloneable JavaDoc {
443
444         //
445
// Data
446
//
447

448         /** Axis. */
449         public Axis axis;
450
451         /** Node test. */
452         public NodeTest nodeTest;
453
454         //
455
// Constructors
456
//
457

458         /** Constructs a step from an axis and node test. */
459         public Step(Axis axis, NodeTest nodeTest) {
460             this.axis = axis;
461             this.nodeTest = nodeTest;
462         } // <init>(Axis,NodeTest)
463

464         /** Copy constructor. */
465         protected Step(Step step) {
466             axis = (Axis)step.axis.clone();
467             nodeTest = (NodeTest)step.nodeTest.clone();
468         } // <init>(Step)
469

470         //
471
// Object methods
472
//
473

474         /** Returns a string representation of this object. */
475         public String JavaDoc toString() {
476             if (axis.type == Axis.SELF) {
477                 return ".";
478             }
479             if (axis.type == Axis.ATTRIBUTE) {
480                 return "@" + nodeTest.toString();
481             }
482             if (axis.type == Axis.CHILD) {
483                 return nodeTest.toString();
484             }
485             if (axis.type == Axis.DESCENDANT) {
486                 return "//";
487             }
488             return "??? ("+axis.type+')';
489         } // toString():String
490

491         /** Returns a clone of this object. */
492         public Object JavaDoc clone() {
493             return new Step(this);
494         } // clone():Object
495

496     } // class Step
497

498     /**
499      * Axis.
500      *
501      * @author Andy Clark, IBM
502      */

503     public static class Axis
504         implements Cloneable JavaDoc {
505
506         //
507
// Constants
508
//
509

510         /** Type: child. */
511         public static final short CHILD = 1;
512
513         /** Type: attribute. */
514         public static final short ATTRIBUTE = 2;
515
516         /** Type: self. */
517         public static final short SELF = 3;
518
519
520         /** Type: descendant. */
521         public static final short DESCENDANT = 4;
522         //
523
// Data
524
//
525

526         /** Axis type. */
527         public short type;
528
529         //
530
// Constructors
531
//
532

533         /** Constructs an axis with the specified type. */
534         public Axis(short type) {
535             this.type = type;
536         } // <init>(short)
537

538         /** Copy constructor. */
539         protected Axis(Axis axis) {
540             type = axis.type;
541         } // <init>(Axis)
542

543         //
544
// Object methods
545
//
546

547         /** Returns a string representation of this object. */
548         public String JavaDoc toString() {
549             switch (type) {
550                 case CHILD: return "child";
551                 case ATTRIBUTE: return "attribute";
552                 case SELF: return "self";
553                 case DESCENDANT: return "descendant";
554             }
555             return "???";
556         } // toString():String
557

558         /** Returns a clone of this object. */
559         public Object JavaDoc clone() {
560             return new Axis(this);
561         } // clone():Object
562

563     } // class Axis
564

565     /**
566      * Node test.
567      *
568      * @author Andy Clark, IBM
569      */

570     public static class NodeTest
571         implements Cloneable JavaDoc {
572
573         //
574
// Constants
575
//
576

577         /** Type: qualified name. */
578         public static final short QNAME = 1;
579
580         /** Type: wildcard. */
581         public static final short WILDCARD = 2;
582
583         /** Type: node. */
584         public static final short NODE = 3;
585
586         /** Type: namespace */
587         public static final short NAMESPACE= 4;
588
589         //
590
// Data
591
//
592

593         /** Node test type. */
594         public short type;
595
596         /** Node qualified name. */
597         public final QName name = new QName();
598
599         //
600
// Constructors
601
//
602

603         /** Constructs a node test of type WILDCARD or NODE. */
604         public NodeTest(short type) {
605             this.type = type;
606         } // <init>(int)
607

608         /** Constructs a node test of type QName. */
609         public NodeTest(QName name) {
610             this.type = QNAME;
611             this.name.setValues(name);
612         } // <init>(QName)
613
/** Constructs a node test of type Namespace. */
614         public NodeTest(String JavaDoc prefix, String JavaDoc uri) {
615             this.type = NAMESPACE;
616             this.name.setValues(prefix, null, null, uri);
617         } // <init>(String,String)
618

619         /** Copy constructor. */
620         public NodeTest(NodeTest nodeTest) {
621             type = nodeTest.type;
622             name.setValues(nodeTest.name);
623         } // <init>(NodeTest)
624

625         //
626
// Object methods
627
//
628

629         /** Returns a string representation of this object. */
630         public String JavaDoc toString() {
631
632             switch (type) {
633                 case QNAME: {
634                     if (name.prefix.length() !=0) {
635                         if (name.uri != null) {
636                             return name.prefix+':'+name.localpart;
637                         }
638                         return "{"+name.uri+'}'+name.prefix+':'+name.localpart;
639                     }
640                     return name.localpart;
641                 }
642                 case NAMESPACE: {
643                     if (name.prefix.length() !=0) {
644                         if (name.uri != null) {
645                             return name.prefix+":*";
646                         }
647                         return "{"+name.uri+'}'+name.prefix+":*";
648                     }
649                     return "???:*";
650                 }
651                 case WILDCARD: {
652                     return "*";
653                 }
654                 case NODE: {
655                     return "node()";
656                 }
657             }
658             return "???";
659
660         } // toString():String
661

662         /** Returns a clone of this object. */
663         public Object JavaDoc clone() {
664             return new NodeTest(this);
665         } // clone():Object
666

667     } // class NodeTest
668

669     // xpath implementation
670

671     // NOTE: The XPath implementation classes are kept internal because
672
// this implementation is just a temporary hack until a better
673
// and/or more appropriate implementation can be written.
674
// keeping the code in separate source files would "muddy" the
675
// CVS directory when it's not needed. -Ac
676

677     /**
678      * List of tokens.
679      *
680      * @author Glenn Marcy, IBM
681      * @author Andy Clark, IBM
682      *
683      * @version $Id: XPath.java,v 1.13 2004/02/09 22:50:01 kohsuke Exp $
684      */

685     private static final class Tokens {
686
687         static final boolean DUMP_TOKENS = false;
688
689         /**
690          * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
691          * | NameTest | NodeType | Operator | FunctionName
692          * | AxisName | Literal | Number | VariableReference
693          */

694         public static final int
695             EXPRTOKEN_OPEN_PAREN = 0,
696             EXPRTOKEN_CLOSE_PAREN = 1,
697             EXPRTOKEN_OPEN_BRACKET = 2,
698             EXPRTOKEN_CLOSE_BRACKET = 3,
699             EXPRTOKEN_PERIOD = 4,
700             EXPRTOKEN_DOUBLE_PERIOD = 5,
701             EXPRTOKEN_ATSIGN = 6,
702             EXPRTOKEN_COMMA = 7,
703             EXPRTOKEN_DOUBLE_COLON = 8,
704             //
705
// [37] NameTest ::= '*' | NCName ':' '*' | QName
706
//
707
// followed by symbol handle of NCName or QName
708
//
709
EXPRTOKEN_NAMETEST_ANY = 9,
710             EXPRTOKEN_NAMETEST_NAMESPACE = 10,
711             EXPRTOKEN_NAMETEST_QNAME = 11,
712             //
713
// [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
714
//
715
EXPRTOKEN_NODETYPE_COMMENT = 12,
716             EXPRTOKEN_NODETYPE_TEXT = 13,
717             EXPRTOKEN_NODETYPE_PI = 14,
718             EXPRTOKEN_NODETYPE_NODE = 15,
719             //
720
// [32] Operator ::= OperatorName
721
// | MultiplyOperator
722
// | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
723
// [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
724
// [34] MultiplyOperator ::= '*'
725
//
726
EXPRTOKEN_OPERATOR_AND = 16,
727             EXPRTOKEN_OPERATOR_OR = 17,
728             EXPRTOKEN_OPERATOR_MOD = 18,
729             EXPRTOKEN_OPERATOR_DIV = 19,
730             EXPRTOKEN_OPERATOR_MULT = 20,
731             EXPRTOKEN_OPERATOR_SLASH = 21,
732             EXPRTOKEN_OPERATOR_DOUBLE_SLASH = 22,
733             EXPRTOKEN_OPERATOR_UNION = 23,
734             EXPRTOKEN_OPERATOR_PLUS = 24,
735             EXPRTOKEN_OPERATOR_MINUS = 25,
736             EXPRTOKEN_OPERATOR_EQUAL = 26,
737             EXPRTOKEN_OPERATOR_NOT_EQUAL = 27,
738             EXPRTOKEN_OPERATOR_LESS = 28,
739             EXPRTOKEN_OPERATOR_LESS_EQUAL = 29,
740             EXPRTOKEN_OPERATOR_GREATER = 30,
741             EXPRTOKEN_OPERATOR_GREATER_EQUAL = 31,
742
743             //EXPRTOKEN_FIRST_OPERATOR = EXPRTOKEN_OPERATOR_AND,
744
//EXPRTOKEN_LAST_OPERATOR = EXPRTOKEN_OPERATOR_GREATER_EQUAL,
745

746             //
747
// [35] FunctionName ::= QName - NodeType
748
//
749
// followed by symbol handle
750
//
751
EXPRTOKEN_FUNCTION_NAME = 32,
752             //
753
// [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
754
// | 'attribute'
755
// | 'child'
756
// | 'descendant' | 'descendant-or-self'
757
// | 'following' | 'following-sibling'
758
// | 'namespace'
759
// | 'parent'
760
// | 'preceding' | 'preceding-sibling'
761
// | 'self'
762
//
763
EXPRTOKEN_AXISNAME_ANCESTOR = 33,
764             EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF = 34,
765             EXPRTOKEN_AXISNAME_ATTRIBUTE = 35,
766             EXPRTOKEN_AXISNAME_CHILD = 36,
767             EXPRTOKEN_AXISNAME_DESCENDANT = 37,
768             EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF = 38,
769             EXPRTOKEN_AXISNAME_FOLLOWING = 39,
770             EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING = 40,
771             EXPRTOKEN_AXISNAME_NAMESPACE = 41,
772             EXPRTOKEN_AXISNAME_PARENT = 42,
773             EXPRTOKEN_AXISNAME_PRECEDING = 43,
774             EXPRTOKEN_AXISNAME_PRECEDING_SIBLING = 44,
775             EXPRTOKEN_AXISNAME_SELF = 45,
776             //
777
// [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
778
//
779
// followed by symbol handle for literal
780
//
781
EXPRTOKEN_LITERAL = 46,
782             //
783
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
784
// [31] Digits ::= [0-9]+
785
//
786
// followed by number handle
787
//
788
EXPRTOKEN_NUMBER = 47,
789             //
790
// [36] VariableReference ::= '$' QName
791
//
792
// followed by symbol handle for QName
793
//
794
EXPRTOKEN_VARIABLE_REFERENCE = 48;
795
796         private static final String JavaDoc[] fgTokenNames = {
797             "EXPRTOKEN_OPEN_PAREN",
798             "EXPRTOKEN_CLOSE_PAREN",
799             "EXPRTOKEN_OPEN_BRACKET",
800             "EXPRTOKEN_CLOSE_BRACKET",
801             "EXPRTOKEN_PERIOD",
802             "EXPRTOKEN_DOUBLE_PERIOD",
803             "EXPRTOKEN_ATSIGN",
804             "EXPRTOKEN_COMMA",
805             "EXPRTOKEN_DOUBLE_COLON",
806             "EXPRTOKEN_NAMETEST_ANY",
807             "EXPRTOKEN_NAMETEST_NAMESPACE",
808             "EXPRTOKEN_NAMETEST_QNAME",
809             "EXPRTOKEN_NODETYPE_COMMENT",
810             "EXPRTOKEN_NODETYPE_TEXT",
811             "EXPRTOKEN_NODETYPE_PI",
812             "EXPRTOKEN_NODETYPE_NODE",
813             "EXPRTOKEN_OPERATOR_AND",
814             "EXPRTOKEN_OPERATOR_OR",
815             "EXPRTOKEN_OPERATOR_MOD",
816             "EXPRTOKEN_OPERATOR_DIV",
817             "EXPRTOKEN_OPERATOR_MULT",
818             "EXPRTOKEN_OPERATOR_SLASH",
819             "EXPRTOKEN_OPERATOR_DOUBLE_SLASH",
820             "EXPRTOKEN_OPERATOR_UNION",
821             "EXPRTOKEN_OPERATOR_PLUS",
822             "EXPRTOKEN_OPERATOR_MINUS",
823             "EXPRTOKEN_OPERATOR_EQUAL",
824             "EXPRTOKEN_OPERATOR_NOT_EQUAL",
825             "EXPRTOKEN_OPERATOR_LESS",
826             "EXPRTOKEN_OPERATOR_LESS_EQUAL",
827             "EXPRTOKEN_OPERATOR_GREATER",
828             "EXPRTOKEN_OPERATOR_GREATER_EQUAL",
829             "EXPRTOKEN_FUNCTION_NAME",
830             "EXPRTOKEN_AXISNAME_ANCESTOR",
831             "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF",
832             "EXPRTOKEN_AXISNAME_ATTRIBUTE",
833             "EXPRTOKEN_AXISNAME_CHILD",
834             "EXPRTOKEN_AXISNAME_DESCENDANT",
835             "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF",
836             "EXPRTOKEN_AXISNAME_FOLLOWING",
837             "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING",
838             "EXPRTOKEN_AXISNAME_NAMESPACE",
839             "EXPRTOKEN_AXISNAME_PARENT",
840             "EXPRTOKEN_AXISNAME_PRECEDING",
841             "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING",
842             "EXPRTOKEN_AXISNAME_SELF",
843             "EXPRTOKEN_LITERAL",
844             "EXPRTOKEN_NUMBER",
845             "EXPRTOKEN_VARIABLE_REFERENCE"
846         };
847
848         /**
849          *
850          */

851         private static final int INITIAL_TOKEN_COUNT = 1 << 8;
852         private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
853         private int fTokenCount = 0; // for writing
854

855         private SymbolTable fSymbolTable;
856
857         // REVISIT: Code something better here. -Ac
858
private java.util.Hashtable JavaDoc fSymbolMapping = new java.util.Hashtable JavaDoc();
859
860         // REVISIT: Code something better here. -Ac
861
private java.util.Hashtable JavaDoc fTokenNames = new java.util.Hashtable JavaDoc();
862
863         /**
864          * Current position in the token list.
865          */

866         private int fCurrentTokenIndex;
867         
868         //
869
// Constructors
870
//
871

872         public Tokens(SymbolTable symbolTable) {
873             fSymbolTable = symbolTable;
874             final String JavaDoc[] symbols = {
875                 "ancestor", "ancestor-or-self", "attribute",
876                 "child", "descendant", "descendant-or-self",
877                 "following", "following-sibling", "namespace",
878                 "parent", "preceding", "preceding-sibling",
879                 "self",
880             };
881             for (int i = 0; i < symbols.length; i++) {
882                 fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]), new Integer JavaDoc(i));
883             }
884             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPEN_PAREN), "EXPRTOKEN_OPEN_PAREN");
885             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_CLOSE_PAREN), "EXPRTOKEN_CLOSE_PAREN");
886             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPEN_BRACKET), "EXPRTOKEN_OPEN_BRACKET");
887             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_CLOSE_BRACKET), "EXPRTOKEN_CLOSE_BRACKET");
888             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_PERIOD), "EXPRTOKEN_PERIOD");
889             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_DOUBLE_PERIOD), "EXPRTOKEN_DOUBLE_PERIOD");
890             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_ATSIGN), "EXPRTOKEN_ATSIGN");
891             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_COMMA), "EXPRTOKEN_COMMA");
892             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_DOUBLE_COLON), "EXPRTOKEN_DOUBLE_COLON");
893             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NAMETEST_ANY), "EXPRTOKEN_NAMETEST_ANY");
894             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NAMETEST_NAMESPACE), "EXPRTOKEN_NAMETEST_NAMESPACE");
895             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NAMETEST_QNAME), "EXPRTOKEN_NAMETEST_QNAME");
896             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NODETYPE_COMMENT), "EXPRTOKEN_NODETYPE_COMMENT");
897             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NODETYPE_TEXT), "EXPRTOKEN_NODETYPE_TEXT");
898             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NODETYPE_PI), "EXPRTOKEN_NODETYPE_PI");
899             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NODETYPE_NODE), "EXPRTOKEN_NODETYPE_NODE");
900             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_AND), "EXPRTOKEN_OPERATOR_AND");
901             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_OR), "EXPRTOKEN_OPERATOR_OR");
902             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_MOD), "EXPRTOKEN_OPERATOR_MOD");
903             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_DIV), "EXPRTOKEN_OPERATOR_DIV");
904             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_MULT), "EXPRTOKEN_OPERATOR_MULT");
905             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_SLASH), "EXPRTOKEN_OPERATOR_SLASH");
906             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_DOUBLE_SLASH), "EXPRTOKEN_OPERATOR_DOUBLE_SLASH");
907             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_UNION), "EXPRTOKEN_OPERATOR_UNION");
908             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_PLUS), "EXPRTOKEN_OPERATOR_PLUS");
909             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_MINUS), "EXPRTOKEN_OPERATOR_MINUS");
910             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_EQUAL), "EXPRTOKEN_OPERATOR_EQUAL");
911             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_NOT_EQUAL), "EXPRTOKEN_OPERATOR_NOT_EQUAL");
912             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_LESS), "EXPRTOKEN_OPERATOR_LESS");
913             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_LESS_EQUAL), "EXPRTOKEN_OPERATOR_LESS_EQUAL");
914             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_GREATER), "EXPRTOKEN_OPERATOR_GREATER");
915             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_OPERATOR_GREATER_EQUAL), "EXPRTOKEN_OPERATOR_GREATER_EQUAL");
916             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_FUNCTION_NAME), "EXPRTOKEN_FUNCTION_NAME");
917             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_ANCESTOR), "EXPRTOKEN_AXISNAME_ANCESTOR");
918             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF), "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF");
919             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_ATTRIBUTE), "EXPRTOKEN_AXISNAME_ATTRIBUTE");
920             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_CHILD), "EXPRTOKEN_AXISNAME_CHILD");
921             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_DESCENDANT), "EXPRTOKEN_AXISNAME_DESCENDANT");
922             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF), "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF");
923             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_FOLLOWING), "EXPRTOKEN_AXISNAME_FOLLOWING");
924             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING), "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING");
925             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_NAMESPACE), "EXPRTOKEN_AXISNAME_NAMESPACE");
926             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_PARENT), "EXPRTOKEN_AXISNAME_PARENT");
927             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_PRECEDING), "EXPRTOKEN_AXISNAME_PRECEDING");
928             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_PRECEDING_SIBLING), "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING");
929             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_AXISNAME_SELF), "EXPRTOKEN_AXISNAME_SELF");
930             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_LITERAL), "EXPRTOKEN_LITERAL");
931             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_NUMBER), "EXPRTOKEN_NUMBER");
932             fTokenNames.put(new Integer JavaDoc(EXPRTOKEN_VARIABLE_REFERENCE), "EXPRTOKEN_VARIABLE_REFERENCE");
933         }
934
935         //
936
// Public methods
937
//
938

939 // public String getTokenName(int token) {
940
// if (token < 0 || token >= fgTokenNames.length)
941
// return null;
942
// return fgTokenNames[token];
943
// }
944
//
945
public String JavaDoc getTokenString(int token) {
946             return (String JavaDoc)fTokenNames.get(new Integer JavaDoc(token));
947         }
948
949         public void addToken(String JavaDoc tokenStr) {
950             Integer JavaDoc tokenInt = (Integer JavaDoc)fTokenNames.get(tokenStr);
951             if (tokenInt == null) {
952                 tokenInt = new Integer JavaDoc(fTokenNames.size());
953                 fTokenNames.put(tokenInt, tokenStr);
954             }
955             addToken(tokenInt.intValue());
956         }
957
958         public void addToken(int token) {
959             try {
960                 fTokens[fTokenCount] = token;
961             } catch (ArrayIndexOutOfBoundsException JavaDoc ex) {
962                 int[] oldList = fTokens;
963                 fTokens = new int[fTokenCount << 1];
964                 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
965                 fTokens[fTokenCount] = token;
966             }
967             fTokenCount++;
968         }
969 // public int getTokenCount() {
970
// return fTokenCount;
971
// }
972
// public int getToken(int tokenIndex) {
973
// return fTokens[tokenIndex];
974
// }
975

976         /**
977          * Resets the current position to the head of the token list.
978          */

979         public void rewind() {
980             fCurrentTokenIndex=0;
981         }
982         /**
983          * Returns true if the {@link #getNextToken()} method
984          * returns a valid token.
985          */

986         public boolean hasMore() {
987             return fCurrentTokenIndex<fTokenCount;
988         }
989         /**
990          * Obtains the token at the current position, then advance
991          * the current position by one.
992          *
993          * If there's no such next token, this method throws
994          * <tt>new XPathException("c-general-xpath");</tt>.
995          */

996         public int nextToken() throws XPathException {
997             if( fCurrentTokenIndex==fTokenCount )
998                 throw new XPathException("c-general-xpath");
999             return fTokens[fCurrentTokenIndex++];
1000        }
1001        /**
1002         * Obtains the token at the current position, without advancing
1003         * the current position.
1004         *
1005         * If there's no such next token, this method throws
1006         * <tt>new XPathException("c-general-xpath");</tt>.
1007         */

1008        public int peekToken() throws XPathException {
1009            if( fCurrentTokenIndex==fTokenCount )
1010                throw new XPathException("c-general-xpath");
1011            return fTokens[fCurrentTokenIndex];
1012        }
1013        /**
1014         * Obtains the token at the current position as a String.
1015         *
1016         * If there's no current token or if the current token
1017         * is not a string token, this method throws
1018         * <tt>new XPathException("c-general-xpath");</tt>.
1019         */

1020        public String JavaDoc nextTokenAsString() throws XPathException {
1021            String JavaDoc s = getTokenString(nextToken());
1022            if(s==null) throw new XPathException("c-general-xpath");
1023            return s;
1024        }
1025        
1026        public void dumpTokens() {
1027            //if (DUMP_TOKENS) {
1028
for (int i = 0; i < fTokenCount; i++) {
1029                    switch (fTokens[i]) {
1030                    case EXPRTOKEN_OPEN_PAREN:
1031                        System.out.print("<OPEN_PAREN/>");
1032                        break;
1033                    case EXPRTOKEN_CLOSE_PAREN:
1034                        System.out.print("<CLOSE_PAREN/>");
1035                        break;
1036                    case EXPRTOKEN_OPEN_BRACKET:
1037                        System.out.print("<OPEN_BRACKET/>");
1038                        break;
1039                    case EXPRTOKEN_CLOSE_BRACKET:
1040                        System.out.print("<CLOSE_BRACKET/>");
1041                        break;
1042                    case EXPRTOKEN_PERIOD:
1043                        System.out.print("<PERIOD/>");
1044                        break;
1045                    case EXPRTOKEN_DOUBLE_PERIOD:
1046                        System.out.print("<DOUBLE_PERIOD/>");
1047                        break;
1048                    case EXPRTOKEN_ATSIGN:
1049                        System.out.print("<ATSIGN/>");
1050                        break;
1051                    case EXPRTOKEN_COMMA:
1052                        System.out.print("<COMMA/>");
1053                        break;
1054                    case EXPRTOKEN_DOUBLE_COLON:
1055                        System.out.print("<DOUBLE_COLON/>");
1056                        break;
1057                    case EXPRTOKEN_NAMETEST_ANY:
1058                        System.out.print("<NAMETEST_ANY/>");
1059                        break;
1060                    case EXPRTOKEN_NAMETEST_NAMESPACE:
1061                        System.out.print("<NAMETEST_NAMESPACE");
1062                        System.out.print(" prefix=\"" + getTokenString(fTokens[++i]) + "\"");
1063                        System.out.print("/>");
1064                        break;
1065                    case EXPRTOKEN_NAMETEST_QNAME:
1066                        System.out.print("<NAMETEST_QNAME");
1067                        if (fTokens[++i] != -1)
1068                            System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
1069                        System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
1070                        System.out.print("/>");
1071                        break;
1072                    case EXPRTOKEN_NODETYPE_COMMENT:
1073                        System.out.print("<NODETYPE_COMMENT/>");
1074                        break;
1075                    case EXPRTOKEN_NODETYPE_TEXT:
1076                        System.out.print("<NODETYPE_TEXT/>");
1077                        break;
1078                    case EXPRTOKEN_NODETYPE_PI:
1079                        System.out.print("<NODETYPE_PI/>");
1080                        break;
1081                    case EXPRTOKEN_NODETYPE_NODE:
1082                        System.out.print("<NODETYPE_NODE/>");
1083                        break;
1084                    case EXPRTOKEN_OPERATOR_AND:
1085                        System.out.print("<OPERATOR_AND/>");
1086                        break;
1087                    case EXPRTOKEN_OPERATOR_OR:
1088                        System.out.print("<OPERATOR_OR/>");
1089                        break;
1090                    case EXPRTOKEN_OPERATOR_MOD:
1091                        System.out.print("<OPERATOR_MOD/>");
1092                        break;
1093                    case EXPRTOKEN_OPERATOR_DIV:
1094                        System.out.print("<OPERATOR_DIV/>");
1095                        break;
1096                    case EXPRTOKEN_OPERATOR_MULT:
1097                        System.out.print("<OPERATOR_MULT/>");
1098                        break;
1099                    case EXPRTOKEN_OPERATOR_SLASH:
1100                        System.out.print("<OPERATOR_SLASH/>");
1101                        if (i + 1 < fTokenCount) {
1102                            System.out.println();
1103                            System.out.print(" ");
1104                        }
1105                        break;
1106                    case EXPRTOKEN_OPERATOR_DOUBLE_SLASH:
1107                        System.out.print("<OPERATOR_DOUBLE_SLASH/>");
1108                        break;
1109                    case EXPRTOKEN_OPERATOR_UNION:
1110                        System.out.print("<OPERATOR_UNION/>");
1111                        break;
1112                    case EXPRTOKEN_OPERATOR_PLUS:
1113                        System.out.print("<OPERATOR_PLUS/>");
1114                        break;
1115                    case EXPRTOKEN_OPERATOR_MINUS:
1116                        System.out.print("<OPERATOR_MINUS/>");
1117                        break;
1118                    case EXPRTOKEN_OPERATOR_EQUAL:
1119                        System.out.print("<OPERATOR_EQUAL/>");
1120                        break;
1121                    case EXPRTOKEN_OPERATOR_NOT_EQUAL:
1122                        System.out.print("<OPERATOR_NOT_EQUAL/>");
1123                        break;
1124                    case EXPRTOKEN_OPERATOR_LESS:
1125                        System.out.print("<OPERATOR_LESS/>");
1126                        break;
1127                    case EXPRTOKEN_OPERATOR_LESS_EQUAL:
1128                        System.out.print("<OPERATOR_LESS_EQUAL/>");
1129                        break;
1130                    case EXPRTOKEN_OPERATOR_GREATER:
1131                        System.out.print("<OPERATOR_GREATER/>");
1132                        break;
1133                    case EXPRTOKEN_OPERATOR_GREATER_EQUAL:
1134                        System.out.print("<OPERATOR_GREATER_EQUAL/>");
1135                        break;
1136                    case EXPRTOKEN_FUNCTION_NAME:
1137                        System.out.print("<FUNCTION_NAME");
1138                        if (fTokens[++i] != -1)
1139                            System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
1140                        System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
1141                        System.out.print("/>");
1142                        break;
1143                    case EXPRTOKEN_AXISNAME_ANCESTOR:
1144                        System.out.print("<AXISNAME_ANCESTOR/>");
1145                        break;
1146                    case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
1147                        System.out.print("<AXISNAME_ANCESTOR_OR_SELF/>");
1148                        break;
1149                    case EXPRTOKEN_AXISNAME_ATTRIBUTE:
1150                        System.out.print("<AXISNAME_ATTRIBUTE/>");
1151                        break;
1152                    case EXPRTOKEN_AXISNAME_CHILD:
1153                        System.out.print("<AXISNAME_CHILD/>");
1154                        break;
1155                    case EXPRTOKEN_AXISNAME_DESCENDANT:
1156                        System.out.print("<AXISNAME_DESCENDANT/>");
1157                        break;
1158                    case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
1159                        System.out.print("<AXISNAME_DESCENDANT_OR_SELF/>");
1160                        break;
1161                    case EXPRTOKEN_AXISNAME_FOLLOWING:
1162                        System.out.print("<AXISNAME_FOLLOWING/>");
1163                        break;
1164                    case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
1165                        System.out.print("<AXISNAME_FOLLOWING_SIBLING/>");
1166                        break;
1167                    case EXPRTOKEN_AXISNAME_NAMESPACE:
1168                        System.out.print("<AXISNAME_NAMESPACE/>");
1169                        break;
1170                    case EXPRTOKEN_AXISNAME_PARENT:
1171                        System.out.print("<AXISNAME_PARENT/>");
1172                        break;
1173                    case EXPRTOKEN_AXISNAME_PRECEDING:
1174                        System.out.print("<AXISNAME_PRECEDING/>");
1175                        break;
1176                    case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
1177                        System.out.print("<AXISNAME_PRECEDING_SIBLING/>");
1178                        break;
1179                    case EXPRTOKEN_AXISNAME_SELF:
1180                        System.out.print("<AXISNAME_SELF/>");
1181                        break;
1182                    case EXPRTOKEN_LITERAL:
1183                        System.out.print("<LITERAL");
1184                        System.out.print(" value=\"" + getTokenString(fTokens[++i]) + "\"");
1185                        System.out.print("/>");
1186                        break;
1187                    case EXPRTOKEN_NUMBER:
1188                        System.out.print("<NUMBER");
1189                        System.out.print(" whole=\"" + getTokenString(fTokens[++i]) + "\"");
1190                        System.out.print(" part=\"" + getTokenString(fTokens[++i]) + "\"");
1191                        System.out.print("/>");
1192                        break;
1193                    case EXPRTOKEN_VARIABLE_REFERENCE:
1194                        System.out.print("<VARIABLE_REFERENCE");
1195                        if (fTokens[++i] != -1)
1196                            System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
1197                        System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
1198                        System.out.print("/>");
1199                        break;
1200                    default:
1201                        System.out.println("<???/>");
1202                    }
1203                }
1204                System.out.println();
1205            //}
1206
}
1207
1208    } // class Tokens
1209

1210    /**
1211     * @author Glenn Marcy, IBM
1212     * @author Andy Clark, IBM
1213     *
1214     * @version $Id: XPath.java,v 1.13 2004/02/09 22:50:01 kohsuke Exp $
1215     */

1216    private static class Scanner {
1217
1218        /**
1219         * 7-bit ASCII subset
1220         *
1221         * 0 1 2 3 4 5 6 7 8 9 A B C D E F
1222         * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
1223         * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
1224         * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
1225         * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
1226         * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
1227         * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
1228         * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
1229         * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
1230         */

1231        private static final byte
1232            CHARTYPE_INVALID = 0, // invalid XML character
1233
CHARTYPE_OTHER = 1, // not special - one of "#%&;?\^`{}~" or DEL
1234
CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
1235
CHARTYPE_EXCLAMATION = 3, // '!' (0x21)
1236
CHARTYPE_QUOTE = 4, // '\"' or '\'' (0x22 and 0x27)
1237
CHARTYPE_DOLLAR = 5, // '$' (0x24)
1238
CHARTYPE_OPEN_PAREN = 6, // '(' (0x28)
1239
CHARTYPE_CLOSE_PAREN = 7, // ')' (0x29)
1240
CHARTYPE_STAR = 8, // '*' (0x2A)
1241
CHARTYPE_PLUS = 9, // '+' (0x2B)
1242
CHARTYPE_COMMA = 10, // ',' (0x2C)
1243
CHARTYPE_MINUS = 11, // '-' (0x2D)
1244
CHARTYPE_PERIOD = 12, // '.' (0x2E)
1245
CHARTYPE_SLASH = 13, // '/' (0x2F)
1246
CHARTYPE_DIGIT = 14, // '0'-'9' (0x30 to 0x39)
1247
CHARTYPE_COLON = 15, // ':' (0x3A)
1248
CHARTYPE_LESS = 16, // '<' (0x3C)
1249
CHARTYPE_EQUAL = 17, // '=' (0x3D)
1250
CHARTYPE_GREATER = 18, // '>' (0x3E)
1251
CHARTYPE_ATSIGN = 19, // '@' (0x40)
1252
CHARTYPE_LETTER = 20, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
1253
CHARTYPE_OPEN_BRACKET = 21, // '[' (0x5B)
1254
CHARTYPE_CLOSE_BRACKET = 22, // ']' (0x5D)
1255
CHARTYPE_UNDERSCORE = 23, // '_' (0x5F)
1256
CHARTYPE_UNION = 24, // '|' (0x7C)
1257
CHARTYPE_NONASCII = 25; // Non-ASCII Unicode codepoint (>= 0x80)
1258

1259        private static final byte[] fASCIICharMap = {
1260            0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0,
1261            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1262            2, 3, 4, 1, 5, 1, 1, 4, 6, 7, 8, 9, 10, 11, 12, 13,
1263           14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 1, 16, 17, 18, 1,
1264           19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
1265           20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 1, 22, 1, 23,
1266            1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
1267           20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1, 24, 1, 1, 1
1268        };
1269
1270        /**
1271         * Symbol literals
1272         */

1273
1274        //
1275
// Data
1276
//
1277

1278        /** Symbol table. */
1279        private SymbolTable fSymbolTable;
1280
1281        // symbols
1282

1283        private static final String JavaDoc fAndSymbol = "and".intern();
1284        private static final String JavaDoc fOrSymbol = "or".intern();
1285        private static final String JavaDoc fModSymbol = "mod".intern();
1286        private static final String JavaDoc fDivSymbol = "div".intern();
1287
1288        private static final String JavaDoc fCommentSymbol = "comment".intern();
1289        private static final String JavaDoc fTextSymbol = "text".intern();
1290        private static final String JavaDoc fPISymbol = "processing-instruction".intern();
1291        private static final String JavaDoc fNodeSymbol = "node".intern();
1292
1293        private static final String JavaDoc fAncestorSymbol = "ancestor".intern();
1294        private static final String JavaDoc fAncestorOrSelfSymbol = "ancestor-or-self".intern();
1295        private static final String JavaDoc fAttributeSymbol = "attribute".intern();
1296        private static final String JavaDoc fChildSymbol = "child".intern();
1297        private static final String JavaDoc fDescendantSymbol = "descendant".intern();
1298        private static final String JavaDoc fDescendantOrSelfSymbol = "descendant-or-self".intern();
1299        private static final String JavaDoc fFollowingSymbol = "following".intern();
1300        private static final String JavaDoc fFollowingSiblingSymbol = "following-sibling".intern();
1301        private static final String JavaDoc fNamespaceSymbol = "namespace".intern();
1302        private static final String JavaDoc fParentSymbol = "parent".intern();
1303        private static final String JavaDoc fPrecedingSymbol = "preceding".intern();
1304        private static final String JavaDoc fPrecedingSiblingSymbol = "preceding-sibling".intern();
1305        private static final String JavaDoc fSelfSymbol = "self".intern();
1306
1307        //
1308
// Constructors
1309
//
1310

1311        /** Constructs an XPath expression scanner. */
1312        public Scanner(SymbolTable symbolTable) {
1313
1314            // save pool and tokens
1315
fSymbolTable = symbolTable;
1316
1317        } // <init>(SymbolTable)
1318

1319        /**
1320         *
1321         */

1322        public boolean scanExpr(SymbolTable symbolTable,
1323                                XPath.Tokens tokens, String JavaDoc data,
1324                                int currentOffset, int endOffset)
1325            throws XPathException {
1326
1327            int nameOffset;
1328            String JavaDoc nameHandle, prefixHandle;
1329            boolean starIsMultiplyOperator = false;
1330            int ch;
1331
1332            while (true) {
1333                if (currentOffset == endOffset) {
1334                    break;
1335                }
1336                ch = data.charAt(currentOffset);
1337                //
1338
// [39] ExprWhitespace ::= S
1339
//
1340
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1341                    if (++currentOffset == endOffset) {
1342                        break;
1343                    }
1344                    ch = data.charAt(currentOffset);
1345                }
1346                if (currentOffset == endOffset) {
1347                    break;
1348                }
1349                //
1350
// [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
1351
// | NameTest | NodeType | Operator | FunctionName
1352
// | AxisName | Literal | Number | VariableReference
1353
//
1354
byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch];
1355                switch (chartype) {
1356                case CHARTYPE_OPEN_PAREN: // '('
1357
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1358                    starIsMultiplyOperator = false;
1359                    if (++currentOffset == endOffset) {
1360                        break;
1361                    }
1362                    break;
1363                case CHARTYPE_CLOSE_PAREN: // ')'
1364
addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN);
1365                    starIsMultiplyOperator = true;
1366                    if (++currentOffset == endOffset) {
1367                        break;
1368                    }
1369                    break;
1370                case CHARTYPE_OPEN_BRACKET: // '['
1371
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_BRACKET);
1372                    starIsMultiplyOperator = false;
1373                    if (++currentOffset == endOffset) {
1374                        break;
1375                    }
1376                    break;
1377                case CHARTYPE_CLOSE_BRACKET: // ']'
1378
addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET);
1379                    starIsMultiplyOperator = true;
1380                    if (++currentOffset == endOffset) {
1381                        break;
1382                    }
1383                    break;
1384                //
1385
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1386
// ^^^^^^^^^^
1387
//
1388
case CHARTYPE_PERIOD: // '.', '..' or '.' Digits
1389
if (currentOffset + 1 == endOffset) {
1390                        addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1391                        starIsMultiplyOperator = true;
1392                        currentOffset++;
1393                        break;
1394                    }
1395                    ch = data.charAt(currentOffset + 1);
1396                    if (ch == '.') { // '..'
1397
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD);
1398                        starIsMultiplyOperator = true;
1399                        currentOffset += 2;
1400                    } else if (ch >= '0' && ch <= '9') {
1401                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1402                        starIsMultiplyOperator = true;
1403                        currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
1404                    } else if (ch == '/') {
1405                        addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1406                        starIsMultiplyOperator = true;
1407                        currentOffset++;
1408                    } else if (ch == '|') {
1409                        addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1410                        starIsMultiplyOperator = true;
1411                        currentOffset++;
1412                        break;
1413                    } else if (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1414                        // this is legal if the next token is non-existent or |
1415
do {
1416                            if (++currentOffset == endOffset) {
1417                                break;
1418                            }
1419                            ch = data.charAt(currentOffset);
1420                        } while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D);
1421                        if (currentOffset == endOffset || ch == '|') {
1422                            addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1423                            starIsMultiplyOperator = true;
1424                            break;
1425                        }
1426                        throw new XPathException ("c-general-xpath");
1427                    } else { // '.'
1428
throw new XPathException ("c-general-xpath");
1429                    }
1430                    if (currentOffset == endOffset) {
1431                        break;
1432                    }
1433                    break;
1434                case CHARTYPE_ATSIGN: // '@'
1435
addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN);
1436                    starIsMultiplyOperator = false;
1437                    if (++currentOffset == endOffset) {
1438                        break;
1439                    }
1440                    break;
1441                case CHARTYPE_COMMA: // ','
1442
addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA);
1443                    starIsMultiplyOperator = false;
1444                    if (++currentOffset == endOffset) {
1445                        break;
1446                    }
1447                    break;
1448                case CHARTYPE_COLON: // '::'
1449
if (++currentOffset == endOffset) {
1450                // System.out.println("abort 1a");
1451
return false; // REVISIT
1452
}
1453                    ch = data.charAt(currentOffset);
1454                    if (ch != ':') {
1455                // System.out.println("abort 1b");
1456
return false; // REVISIT
1457
}
1458                    addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1459                    starIsMultiplyOperator = false;
1460                    if (++currentOffset == endOffset) {
1461                        break;
1462                    }
1463                    break;
1464                case CHARTYPE_SLASH: // '/' and '//'
1465
if (++currentOffset == endOffset) {
1466                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1467                        starIsMultiplyOperator = false;
1468                        break;
1469                    }
1470                    ch = data.charAt(currentOffset);
1471                    if (ch == '/') { // '//'
1472
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH);
1473                        starIsMultiplyOperator = false;
1474                        if (++currentOffset == endOffset) {
1475                            break;
1476                        }
1477                    } else {
1478                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1479                        starIsMultiplyOperator = false;
1480                    }
1481                    break;
1482                case CHARTYPE_UNION: // '|'
1483
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_UNION);
1484                    starIsMultiplyOperator = false;
1485                    if (++currentOffset == endOffset) {
1486                        break;
1487                    }
1488                    break;
1489                case CHARTYPE_PLUS: // '+'
1490
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS);
1491                    starIsMultiplyOperator = false;
1492                    if (++currentOffset == endOffset) {
1493                        break;
1494                    }
1495                    break;
1496                case CHARTYPE_MINUS: // '-'
1497
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS);
1498                    starIsMultiplyOperator = false;
1499                    if (++currentOffset == endOffset) {
1500                        break;
1501                    }
1502                    break;
1503                case CHARTYPE_EQUAL: // '='
1504
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL);
1505                    starIsMultiplyOperator = false;
1506                    if (++currentOffset == endOffset) {
1507                        break;
1508                    }
1509                    break;
1510                case CHARTYPE_EXCLAMATION: // '!='
1511
if (++currentOffset == endOffset) {
1512                // System.out.println("abort 2a");
1513
return false; // REVISIT
1514
}
1515                    ch = data.charAt(currentOffset);
1516                    if (ch != '=') {
1517                // System.out.println("abort 2b");
1518
return false; // REVISIT
1519
}
1520                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL);
1521                    starIsMultiplyOperator = false;
1522                    if (++currentOffset == endOffset) {
1523                        break;
1524                    }
1525                    break;
1526                case CHARTYPE_LESS: // '<' and '<='
1527
if (++currentOffset == endOffset) {
1528                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1529                        starIsMultiplyOperator = false;
1530                        break;
1531                    }
1532                    ch = data.charAt(currentOffset);
1533                    if (ch == '=') { // '<='
1534
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL);
1535                        starIsMultiplyOperator = false;
1536                        if (++currentOffset == endOffset) {
1537                            break;
1538                        }
1539                    } else {
1540                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1541                        starIsMultiplyOperator = false;
1542                    }
1543                    break;
1544                case CHARTYPE_GREATER: // '>' and '>='
1545
if (++currentOffset == endOffset) {
1546                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1547                        starIsMultiplyOperator = false;
1548                        break;
1549                    }
1550                    ch = data.charAt(currentOffset);
1551                    if (ch == '=') { // '>='
1552
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL);
1553                        starIsMultiplyOperator = false;
1554                        if (++currentOffset == endOffset) {
1555                            break;
1556                        }
1557                    } else {
1558                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1559                        starIsMultiplyOperator = false;
1560                    }
1561                    break;
1562                //
1563
// [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
1564
//
1565
case CHARTYPE_QUOTE: // '\"' or '\''
1566
int qchar = ch;
1567                    if (++currentOffset == endOffset) {
1568                // System.out.println("abort 2c");
1569
return false; // REVISIT
1570
}
1571                    ch = data.charAt(currentOffset);
1572                    int litOffset = currentOffset;
1573                    while (ch != qchar) {
1574                        if (++currentOffset == endOffset) {
1575                // System.out.println("abort 2d");
1576
return false; // REVISIT
1577
}
1578                        ch = data.charAt(currentOffset);
1579                    }
1580                    int litLength = currentOffset - litOffset;
1581                    addToken(tokens, XPath.Tokens.EXPRTOKEN_LITERAL);
1582                    starIsMultiplyOperator = true;
1583                    tokens.addToken(symbolTable.addSymbol(data.substring(litOffset, litOffset + litLength)));
1584                    if (++currentOffset == endOffset) {
1585                        break;
1586                    }
1587                    break;
1588                //
1589
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1590
// [31] Digits ::= [0-9]+
1591
//
1592
case CHARTYPE_DIGIT:
1593                    addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1594                    starIsMultiplyOperator = true;
1595                    currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
1596                    break;
1597                //
1598
// [36] VariableReference ::= '$' QName
1599
//
1600
case CHARTYPE_DOLLAR:
1601                    if (++currentOffset == endOffset) {
1602                // System.out.println("abort 3a");
1603
return false; // REVISIT
1604
}
1605                    nameOffset = currentOffset;
1606                    currentOffset = scanNCName(data, endOffset, currentOffset);
1607                    if (currentOffset == nameOffset) {
1608                // System.out.println("abort 3b");
1609
return false; // REVISIT
1610
}
1611                    if (currentOffset < endOffset) {
1612                        ch = data.charAt(currentOffset);
1613                    }
1614                    else {
1615                        ch = -1;
1616                    }
1617                    nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1618                    if (ch != ':') {
1619                        prefixHandle = XMLSymbols.EMPTY_STRING;
1620                    } else {
1621                        prefixHandle = nameHandle;
1622                        if (++currentOffset == endOffset) {
1623                // System.out.println("abort 4a");
1624
return false; // REVISIT
1625
}
1626                        nameOffset = currentOffset;
1627                        currentOffset = scanNCName(data, endOffset, currentOffset);
1628                        if (currentOffset == nameOffset) {
1629                // System.out.println("abort 4b");
1630
return false; // REVISIT
1631
}
1632                        if (currentOffset < endOffset) {
1633                            ch = data.charAt(currentOffset);
1634                        }
1635                        else {
1636                            ch = -1;
1637                        }
1638                        nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1639                    }
1640                    addToken(tokens, XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE);
1641                    starIsMultiplyOperator = true;
1642                    tokens.addToken(prefixHandle);
1643                    tokens.addToken(nameHandle);
1644                    break;
1645                //
1646
// [37] NameTest ::= '*' | NCName ':' '*' | QName
1647
// [34] MultiplyOperator ::= '*'
1648
//
1649
case CHARTYPE_STAR: // '*'
1650
//
1651
// 3.7 Lexical Structure
1652
//
1653
// If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1654
// an Operator, then a * must be recognized as a MultiplyOperator.
1655
//
1656
// Otherwise, the token must not be recognized as a MultiplyOperator.
1657
//
1658
if (starIsMultiplyOperator) {
1659                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MULT);
1660                        starIsMultiplyOperator = false;
1661                    } else {
1662                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_ANY);
1663                        starIsMultiplyOperator = true;
1664                    }
1665                    if (++currentOffset == endOffset) {
1666                        break;
1667                    }
1668                    break;
1669                //
1670
// NCName, QName and non-terminals
1671
//
1672
case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic)
1673
case CHARTYPE_LETTER:
1674                case CHARTYPE_UNDERSCORE:
1675                    //
1676
// 3.7 Lexical Structure
1677
//
1678
// If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1679
// an Operator, then an NCName must be recognized as an OperatorName.
1680
//
1681
// If the character following an NCName (possibly after intervening ExprWhitespace) is (,
1682
// then the token must be recognized as a NodeType or a FunctionName.
1683
//
1684
// If the two characters following an NCName (possibly after intervening ExprWhitespace)
1685
// are ::, then the token must be recognized as an AxisName.
1686
//
1687
// Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1688
// FunctionName, or an AxisName.
1689
//
1690
// [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
1691
// [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
1692
// [35] FunctionName ::= QName - NodeType
1693
// [6] AxisName ::= (see above)
1694
//
1695
// [37] NameTest ::= '*' | NCName ':' '*' | QName
1696
// [5] NCName ::= (Letter | '_') (NCNameChar)*
1697
// [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar')
1698
// [?] QName ::= (NCName ':')? NCName
1699
// [?] Letter ::= [A-Za-z] (ascii subset of 'Letter')
1700
// [?] Digit ::= [0-9] (ascii subset of 'Digit')
1701
//
1702
nameOffset = currentOffset;
1703                    currentOffset = scanNCName(data, endOffset, currentOffset);
1704                    if (currentOffset == nameOffset) {
1705                // System.out.println("abort 4c");
1706
return false; // REVISIT
1707
}
1708                    if (currentOffset < endOffset) {
1709                        ch = data.charAt(currentOffset);
1710                    }
1711                    else {
1712                        ch = -1;
1713                    }
1714                    nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1715                    boolean isNameTestNCName = false;
1716                    boolean isAxisName = false;
1717                    prefixHandle = XMLSymbols.EMPTY_STRING;
1718                    if (ch == ':') {
1719                        if (++currentOffset == endOffset) {
1720                // System.out.println("abort 5");
1721
return false; // REVISIT
1722
}
1723                        ch = data.charAt(currentOffset);
1724                        if (ch == '*') {
1725                            if (++currentOffset < endOffset) {
1726                                ch = data.charAt(currentOffset);
1727                            }
1728                            isNameTestNCName = true;
1729                        } else if (ch == ':') {
1730                            if (++currentOffset < endOffset) {
1731                                ch = data.charAt(currentOffset);
1732                            }
1733                            isAxisName = true;
1734                        } else {
1735                            prefixHandle = nameHandle;
1736                            nameOffset = currentOffset;
1737                            currentOffset = scanNCName(data, endOffset, currentOffset);
1738                            if (currentOffset == nameOffset) {
1739                // System.out.println("abort 5b");
1740
return false; // REVISIT
1741
}
1742                            if (currentOffset < endOffset) {
1743                                ch = data.charAt(currentOffset);
1744                            }
1745                            else {
1746                                ch = -1;
1747                            }
1748                            nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1749                        }
1750                    }
1751                    //
1752
// [39] ExprWhitespace ::= S
1753
//
1754
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1755                        if (++currentOffset == endOffset) {
1756                            break;
1757                        }
1758                        ch = data.charAt(currentOffset);
1759                    }
1760                    //
1761
// If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1762
// an Operator, then an NCName must be recognized as an OperatorName.
1763
//
1764
if (starIsMultiplyOperator) {
1765                        if (nameHandle == fAndSymbol) {
1766                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_AND);
1767                            starIsMultiplyOperator = false;
1768                        } else if (nameHandle == fOrSymbol) {
1769                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_OR);
1770                            starIsMultiplyOperator = false;
1771                        } else if (nameHandle == fModSymbol) {
1772                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MOD);
1773                            starIsMultiplyOperator = false;
1774                        } else if (nameHandle == fDivSymbol) {
1775                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DIV);
1776                            starIsMultiplyOperator = false;
1777                        } else {
1778                // System.out.println("abort 6");
1779
return false; // REVISIT
1780
}
1781                        if (isNameTestNCName) {
1782                // System.out.println("abort 7");
1783
return false; // REVISIT - NCName:* where an OperatorName is required
1784
} else if (isAxisName) {
1785                // System.out.println("abort 8");
1786
return false; // REVISIT - AxisName:: where an OperatorName is required
1787
}
1788                        break;
1789                    }
1790                    //
1791
// If the character following an NCName (possibly after intervening ExprWhitespace) is (,
1792
// then the token must be recognized as a NodeType or a FunctionName.
1793
//
1794
if (ch == '(' && !isNameTestNCName && !isAxisName) {
1795                        if (nameHandle == fCommentSymbol) {
1796                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT);
1797                        } else if (nameHandle == fTextSymbol) {
1798                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT);
1799                        } else if (nameHandle == fPISymbol) {
1800                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_PI);
1801                        } else if (nameHandle == fNodeSymbol) {
1802                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_NODE);
1803                        } else {
1804                            addToken(tokens, XPath.Tokens.EXPRTOKEN_FUNCTION_NAME);
1805                            tokens.addToken(prefixHandle);
1806                            tokens.addToken(nameHandle);
1807                        }
1808                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1809                        starIsMultiplyOperator = false;
1810                        if (++currentOffset == endOffset) {
1811                            break;
1812                        }
1813                        break;
1814                    }
1815                    //
1816
// If the two characters following an NCName (possibly after intervening ExprWhitespace)
1817
// are ::, then the token must be recognized as an AxisName.
1818
//
1819
if (isAxisName ||
1820                        (ch == ':' && currentOffset + 1 < endOffset &&
1821                         data.charAt(currentOffset + 1) == ':')) {
1822                        if (nameHandle == fAncestorSymbol) {
1823                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR);
1824                        } else if (nameHandle == fAncestorOrSelfSymbol) {
1825                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF);
1826                        } else if (nameHandle == fAttributeSymbol) {
1827                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE);
1828                        } else if (nameHandle == fChildSymbol) {
1829                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD);
1830                        } else if (nameHandle == fDescendantSymbol) {
1831                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT);
1832                        } else if (nameHandle == fDescendantOrSelfSymbol) {
1833                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF);
1834                        } else if (nameHandle == fFollowingSymbol) {
1835                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING);
1836                        } else if (nameHandle == fFollowingSiblingSymbol) {
1837                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING);
1838                        } else if (nameHandle == fNamespaceSymbol) {
1839                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE);
1840                        } else if (nameHandle == fParentSymbol) {
1841                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT);
1842                        } else if (nameHandle == fPrecedingSymbol) {
1843                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING);
1844                        } else if (nameHandle == fPrecedingSiblingSymbol) {
1845                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING);
1846                        } else if (nameHandle == fSelfSymbol) {
1847                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_SELF);
1848                        } else {
1849                // System.out.println("abort 9");
1850
return false; // REVISIT
1851
}
1852                        if (isNameTestNCName) {
1853                // System.out.println("abort 10");
1854
return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required
1855
}
1856                        addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1857                        starIsMultiplyOperator = false;
1858                        if (!isAxisName) {
1859                            currentOffset++;
1860                            if (++currentOffset == endOffset) {
1861                                break;
1862                            }
1863                        }
1864                        break;
1865                    }
1866                    //
1867
// Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1868
// FunctionName, or an AxisName.
1869
//
1870
if (isNameTestNCName) {
1871                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE);
1872                        starIsMultiplyOperator = true;
1873                        tokens.addToken(nameHandle);
1874                    } else {
1875                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME);
1876                        starIsMultiplyOperator = true;
1877                        tokens.addToken(prefixHandle);
1878                        tokens.addToken(nameHandle);
1879                    }
1880                    break;
1881                }
1882            }
1883            if (XPath.Tokens.DUMP_TOKENS) {
1884                tokens.dumpTokens();
1885            }
1886            return true;
1887        }
1888        //
1889
// [5] NCName ::= (Letter | '_') (NCNameChar)*
1890
// [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
1891
//
1892
int scanNCName(String JavaDoc data, int endOffset, int currentOffset) {
1893            int ch = data.charAt(currentOffset);
1894            if (ch >= 0x80) {
1895                if (!XMLChar.isNameStart(ch))
1896                /*** // REVISIT: Make sure this is a negation. ***
1897                if ((XMLCharacterProperties.fgCharFlags[ch] &
1898                     XMLCharacterProperties.E_InitialNameCharFlag) == 0)
1899                /***/

1900                {
1901                    return currentOffset;
1902                }
1903            }
1904            else {
1905                byte chartype = fASCIICharMap[ch];
1906                if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
1907                    return currentOffset;
1908                }
1909            }
1910            while (++currentOffset < endOffset) {
1911                ch = data.charAt(currentOffset);
1912                if (ch >= 0x80) {
1913                    if (!XMLChar.isName(ch))
1914                    /*** // REVISIT: Make sure this is a negation. ***
1915                    if ((XMLCharacterProperties.fgCharFlags[ch] &
1916                         XMLCharacterProperties.E_NameCharFlag) == 0)
1917                    /***/

1918                    {
1919                        break;
1920                    }
1921                }
1922                else {
1923                    byte chartype = fASCIICharMap[ch];
1924                    if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
1925                        chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
1926                        chartype != CHARTYPE_UNDERSCORE)
1927                    {
1928                        break;
1929                    }
1930                }
1931            }
1932            return currentOffset;
1933        }
1934        //
1935
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1936
// [31] Digits ::= [0-9]+
1937
//
1938
private int scanNumber(XPath.Tokens tokens, String JavaDoc/*byte[]*/ data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) {
1939            int ch = data.charAt(currentOffset);
1940            int whole = 0;
1941            int part = 0;
1942            while (ch >= '0' && ch <= '9') {
1943                whole = (whole * 10) + (ch - '0');
1944                if (++currentOffset == endOffset) {
1945                    break;
1946                }
1947                ch = data.charAt(currentOffset);
1948            }
1949            if (ch == '.') {
1950                if (++currentOffset < endOffset) {
1951                    int start = currentOffset;
1952                    ch = data.charAt(currentOffset);
1953                    while (ch >= '0' && ch <= '9') {
1954                        part = (part * 10) + (ch - '0');
1955                        if (++currentOffset == endOffset) {
1956                            break;
1957                        }
1958                        ch = data.charAt(currentOffset);
1959                    }
1960                    if (part != 0) {
1961                        /***
1962                        part = tokens.addSymbol(data, start, currentOffset - start, encoding);
1963                        /***/

1964                        throw new RuntimeException JavaDoc("find a solution!");
1965                        //part = fStringPool.addSymbol(data.substring(start, currentOffset));
1966
/***/
1967                    }
1968                }
1969            }
1970            tokens.addToken(whole);
1971            tokens.addToken(part);
1972            return currentOffset;
1973        }
1974
1975        //
1976
// Protected methods
1977
//
1978

1979        /**
1980         * This method adds the specified token to the token list. By
1981         * default, this method allows all tokens. However, subclasses
1982         * of the XPathExprScanner can override this method in order
1983         * to disallow certain tokens from being used in the scanned
1984         * XPath expression. This is a convenient way of allowing only
1985         * a subset of XPath.
1986         */

1987        protected void addToken(XPath.Tokens tokens, int token)
1988            throws XPathException {
1989            tokens.addToken(token);
1990        } // addToken(int)
1991

1992    } // class Scanner
1993

1994    //
1995
// MAIN
1996
//
1997

1998    /** Main program entry. */
1999    public static void main(String JavaDoc[] argv) throws Exception JavaDoc {
2000
2001        for (int i = 0; i < argv.length; i++) {
2002            final String JavaDoc expression = argv[i];
2003            System.out.println("# XPath expression: \""+expression+'"');
2004            try {
2005                SymbolTable symbolTable = new SymbolTable();
2006                XPath xpath = new XPath(expression, symbolTable, null);
2007                System.out.println("expanded xpath: \""+xpath.toString()+'"');
2008            }
2009            catch (XPathException e) {
2010                System.out.println("error: "+e.getMessage());
2011            }
2012        }
2013
2014    } // main(String[])
2015

2016} // class XPath
2017
Popular Tags