1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.impl.xpath;
22
23import com.sun.org.apache.xerces.internal.util.SymbolTable;
24import com.sun.org.apache.xerces.internal.util.XMLChar;
25import com.sun.org.apache.xerces.internal.util.XMLSymbols;
26import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
27import com.sun.org.apache.xerces.internal.xni.QName;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import java.util.stream.Collectors;
34
35/**
36 * Bare minimum XPath parser.
37 *
38 * @xerces.internal
39 *
40 * @author Andy Clark, IBM
41 * @author Sunitha Reddy, Sun Microsystems
42 */
43public class XPath {
44
45    //
46    // Constants
47    //
48
49    private static final boolean DEBUG_ALL = false;
50
51    private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false;
52
53    //
54    // Data
55    //
56
57    /** Expression. */
58    protected final String fExpression;
59
60    /** Symbol table. */
61    protected final SymbolTable fSymbolTable;
62
63    /** Location paths. */
64    protected final LocationPath[] fLocationPaths;
65
66    //
67    // Constructors
68    //
69
70    /** Constructs an XPath object from the specified expression. */
71    public XPath(String xpath, SymbolTable symbolTable,
72                 NamespaceContext context)
73        throws XPathException {
74        fExpression = xpath;
75        fSymbolTable = symbolTable;
76        fLocationPaths = parseExpression(context);
77    } // <init>(String,SymbolTable,NamespaceContext)
78
79    //
80    // Public methods
81    //
82
83    /**
84     * Returns a representation of all location paths for this XPath.
85     * XPath = locationPath ( '|' locationPath)
86     */
87    public LocationPath[] getLocationPaths() {
88        LocationPath[] ret=new LocationPath[fLocationPaths.length];
89        for (int i=0;i<fLocationPaths.length;i++){
90            ret[i]=(LocationPath)fLocationPaths[i].clone();
91        }
92        return ret;
93    } // getLocationPath(LocationPath)
94
95    /** Returns a representation of the first location path for this XPath. */
96    public LocationPath getLocationPath() {
97        return (LocationPath)fLocationPaths[0].clone();
98    } // getLocationPath(LocationPath)
99
100    //
101    // Object methods
102    //
103
104    /** Returns a string representation of this object. */
105    @Override
106    public String toString() {
107        final List<LocationPath> l = Arrays.asList(fLocationPaths);
108        final String s = l.stream()
109                .map(aPath -> aPath.toString())
110                .collect(Collectors.joining("|"));
111
112        return s;
113    } // toString():String
114
115    //
116    // Private methods
117    //
118
119    /**
120     * Used by the {@link #parseExpression(NamespaceContext)} method
121     * to verify the assumption.
122     *
123     * If <tt>b</tt> is false, this method throws XPathException
124     * to report the error.
125     */
126    private static void check( boolean b ) throws XPathException {
127        if(!b)      throw new XPathException("c-general-xpath");
128    }
129
130    /**
131     * Used by the {@link #parseExpression(NamespaceContext)} method
132     * to build a {@link LocationPath} object from the accumulated
133     * {@link Step}s.
134     */
135    private LocationPath buildLocationPath( ArrayList<Step> stepsVector ) throws XPathException {
136        int size = stepsVector.size();
137        check(size!=0);
138        Step[] steps = new Step[size];
139        steps = stepsVector.toArray(steps);
140        stepsVector.clear();
141
142        return new LocationPath(steps);
143    }
144
145    /**
146     * This method is implemented by using the XPathExprScanner and
147     * examining the list of tokens that it returns.
148     */
149    private LocationPath[] parseExpression(final NamespaceContext context)
150        throws XPathException {
151
152        // tokens
153        final XPath.Tokens xtokens = new XPath.Tokens(fSymbolTable);
154
155        // scanner
156        XPath.Scanner scanner = new XPath.Scanner(fSymbolTable) {
157            protected void addToken(XPath.Tokens tokens, int token)
158                throws XPathException {
159                if (
160                    token == XPath.Tokens.EXPRTOKEN_ATSIGN ||
161                    token == XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE ||
162                    token == XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME ||
163                    token == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH ||
164                    token == XPath.Tokens.EXPRTOKEN_PERIOD ||
165                    token == XPath.Tokens.EXPRTOKEN_NAMETEST_ANY ||
166                    token == XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE ||
167                    token == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH ||
168                    token == XPath.Tokens.EXPRTOKEN_OPERATOR_UNION ||
169                    token == XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD ||
170                    token == XPath.Tokens.EXPRTOKEN_DOUBLE_COLON
171                    ) {
172                    super.addToken(tokens, token);
173                    return;
174                }
175                throw new XPathException("c-general-xpath");
176            }
177        };
178
179        int length = fExpression.length();
180
181        boolean success = scanner.scanExpr(fSymbolTable,
182                                           xtokens, fExpression, 0, length);
183        if(!success)
184            throw new XPathException("c-general-xpath");
185
186        //fTokens.dumpTokens();
187        ArrayList<Step> stepsVector = new ArrayList<>();
188        ArrayList<LocationPath> locationPathsVector= new ArrayList<>();
189
190        // true when the next token should be 'Step' (as defined in
191        // the production rule [3] of XML Schema P1 section 3.11.6
192        // if false, we are expecting either '|' or '/'.
193        //
194        // this is to make sure we can detect a token list like
195        // 'abc' '/' '/' 'def' 'ghi'
196        boolean expectingStep = true;
197
198        while (xtokens.hasMore()) {
199            final int token = xtokens.nextToken();
200
201            switch (token) {
202                case  XPath.Tokens.EXPRTOKEN_OPERATOR_UNION :{
203                    check(!expectingStep);
204                    locationPathsVector.add(buildLocationPath(stepsVector));
205                    expectingStep=true;
206                    break;
207                }
208                case XPath.Tokens.EXPRTOKEN_ATSIGN: {
209                    check(expectingStep);
210                    Step step = new Step(
211                            new Axis(Axis.ATTRIBUTE),
212                            parseNodeTest(xtokens.nextToken(),xtokens,context));
213                    stepsVector.add(step);
214                    expectingStep=false;
215                    break;
216                }
217                case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: {
218                    check(expectingStep);
219                    // If we got here we're expecting attribute::
220                    if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
221                        throw new XPathException("c-general-xpath");
222                    }
223                    Step step = new Step(
224                            new Axis(Axis.ATTRIBUTE),
225                            parseNodeTest(xtokens.nextToken(),xtokens,context));
226                    stepsVector.add(step);
227                    expectingStep = false;
228                    break;
229                }
230                case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
231                case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
232                case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: {
233                    check(expectingStep);
234                    Step step = new Step(
235                            new Axis(Axis.CHILD),
236                            parseNodeTest(token,xtokens,context));
237                    stepsVector.add(step);
238                    expectingStep=false;
239                    break;
240                }
241                case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD: {
242                    check(expectingStep);
243                    // If we got here we're expecting child::
244                    if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
245                        throw new XPathException("c-general-xpath");
246                    }
247                    Step step = new Step(
248                            new Axis(Axis.CHILD),
249                            parseNodeTest(xtokens.nextToken(),xtokens,context));
250                    stepsVector.add(step);
251                    expectingStep = false;
252                    break;
253                }
254                case XPath.Tokens.EXPRTOKEN_PERIOD: {
255                    check(expectingStep);
256                    expectingStep=false;
257
258                    // unless this is the first step in this location path,
259                    // there's really no reason to keep them in LocationPath.
260                    // This amounts to shorten "a/././b/./c" to "a/b/c".
261                    // Also, the matcher fails to work correctly if XPath
262                    // has those redundant dots.
263                    if (stepsVector.isEmpty()) {
264                        // build step
265                        Axis axis = new Axis(Axis.SELF);
266                        NodeTest nodeTest = new NodeTest(NodeTest.NODE);
267                        Step step = new Step(axis, nodeTest);
268                        stepsVector.add(step);
269
270                        if( xtokens.hasMore()
271                         && xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH){
272                            // consume '//'
273                            xtokens.nextToken();
274
275                            // build step
276                            axis = new Axis(Axis.DESCENDANT);
277                            nodeTest = new NodeTest(NodeTest.NODE);
278                            step = new Step(axis, nodeTest);
279                            stepsVector.add(step);
280                            expectingStep=true;
281                        }
282                    }
283                    break;
284                }
285                case XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH:{
286                    // this cannot appear in an arbitrary position.
287                    // it is only allowed right after '.' when
288                    // '.' is the first token of a location path.
289                    throw new XPathException("c-general-xpath");
290                }
291                case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON: {
292                    // :: cannot appear in an arbitrary position.
293                    // We only expect this token if the xpath
294                    // contains child:: or attribute::
295                    throw new XPathException("c-general-xpath");
296                }
297                case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: {
298                    check(!expectingStep);
299                    expectingStep=true;
300                    break;
301                }
302                default:
303                    // we should have covered all the tokens that we can possibly see.
304                    throw new InternalError();
305            }
306        }
307
308        check(!expectingStep);
309
310        locationPathsVector.add(buildLocationPath(stepsVector));
311
312        // return location path
313        return locationPathsVector.toArray(new LocationPath[locationPathsVector.size()]);
314
315    } // parseExpression(SymbolTable,NamespaceContext)
316
317    /**
318     * Used by {@link #parseExpression} to parse a node test
319     * from the token list.
320     */
321    private NodeTest parseNodeTest( int typeToken, Tokens xtokens, NamespaceContext context )
322        throws XPathException {
323        switch(typeToken) {
324        case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
325            return new NodeTest(NodeTest.WILDCARD);
326
327        case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
328        case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME:
329            // consume QName token
330            String prefix = xtokens.nextTokenAsString();
331            String uri = null;
332            if (context != null && prefix != XMLSymbols.EMPTY_STRING) {
333                uri = context.getURI(prefix);
334            }
335            if (prefix != XMLSymbols.EMPTY_STRING && context != null && uri == null) {
336                throw new XPathException("c-general-xpath-ns");
337            }
338
339            if (typeToken==XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE)
340                return new NodeTest(prefix,uri);
341
342            String localpart = xtokens.nextTokenAsString();
343            String rawname = prefix != XMLSymbols.EMPTY_STRING
344            ? fSymbolTable.addSymbol(prefix+':'+localpart)
345            : localpart;
346
347            return new NodeTest(new QName(prefix, localpart, rawname, uri));
348
349        default:
350            // assertion error
351            throw new XPathException("c-general-xpath");
352
353        }
354    }
355
356
357    //
358    // Classes
359    //
360
361    // location path information
362
363    /**
364     * A location path representation for an XPath expression.
365     *
366     * @xerces.internal
367     *
368     * @author Andy Clark, IBM
369     */
370    public static class LocationPath
371        implements Cloneable {
372
373        //
374        // Data
375        //
376
377        /** List of steps. */
378        public final Step[] steps;
379
380        //
381        // Constructors
382        //
383
384        /** Creates a location path from a series of steps. */
385        public LocationPath(Step[] steps) {
386            this.steps = steps;
387        } // <init>(Step[])
388
389        /** Copy constructor. */
390        protected LocationPath(LocationPath path) {
391            steps = new Step[path.steps.length];
392            for (int i = 0; i < steps.length; i++) {
393                steps[i] = (Step)path.steps[i].clone();
394            }
395        } // <init>(LocationPath)
396
397        //
398        // Object methods
399        //
400
401        /** Returns a string representation of this object. */
402        public String toString() {
403            StringBuffer str = new StringBuffer();
404            for (int i = 0; i < steps.length; i++) {
405                if (i > 0       && (steps[i-1].axis.type!=Axis.DESCENDANT
406                    && steps[i].axis.type!=Axis.DESCENDANT) ){
407                    str.append('/');
408                }
409                str.append(steps[i].toString());
410            }
411            // DEBUG: This code is just for debugging and should *not*
412            //        be left in because it will mess up hashcodes of
413            //        serialized versions of this object. -Ac
414            if (false) {
415                str.append('[');
416                String s = super.toString();
417                str.append(s.substring(s.indexOf('@')));
418                str.append(']');
419            }
420            return str.toString();
421        } // toString():String
422
423        /** Returns a clone of this object. */
424        public Object clone() {
425            return new LocationPath(this);
426        } // clone():Object
427
428    } // class locationPath
429
430    /**
431     * A location path step comprised of an axis and node test.
432     *
433     * @xerces.internal
434     *
435     * @author Andy Clark, IBM
436     */
437    public static class Step
438        implements Cloneable {
439
440        //
441        // Data
442        //
443
444        /** Axis. */
445        public final Axis axis;
446
447        /** Node test. */
448        public final NodeTest nodeTest;
449
450        //
451        // Constructors
452        //
453
454        /** Constructs a step from an axis and node test. */
455        public Step(Axis axis, NodeTest nodeTest) {
456            this.axis = axis;
457            this.nodeTest = nodeTest;
458        } // <init>(Axis,NodeTest)
459
460        /** Copy constructor. */
461        protected Step(Step step) {
462            axis = (Axis)step.axis.clone();
463            nodeTest = (NodeTest)step.nodeTest.clone();
464        } // <init>(Step)
465
466        //
467        // Object methods
468        //
469
470        /** Returns a string representation of this object. */
471        public String toString() {
472            if (axis.type == Axis.SELF) {
473                return ".";
474            }
475            if (axis.type == Axis.ATTRIBUTE) {
476                return "@" + nodeTest.toString();
477            }
478            if (axis.type == Axis.CHILD) {
479                return nodeTest.toString();
480            }
481            if (axis.type == Axis.DESCENDANT) {
482                return "//";
483            }
484            return "??? ("+axis.type+')';
485        } // toString():String
486
487        /** Returns a clone of this object. */
488        public Object clone() {
489            return new Step(this);
490        } // clone():Object
491
492    } // class Step
493
494    /**
495     * Axis.
496     *
497     * @xerces.internal
498     *
499     * @author Andy Clark, IBM
500     */
501    public static class Axis
502        implements Cloneable {
503
504        //
505        // Constants
506        //
507
508        /** Type: child. */
509        public static final short CHILD = 1;
510
511        /** Type: attribute. */
512        public static final short ATTRIBUTE = 2;
513
514        /** Type: self. */
515        public static final short SELF = 3;
516
517
518        /** Type: descendant. */
519        public static final short DESCENDANT = 4;
520        //
521        // Data
522        //
523
524        /** Axis type. */
525        public final short type;
526
527        //
528        // Constructors
529        //
530
531        /** Constructs an axis with the specified type. */
532        public Axis(short type) {
533            this.type = type;
534        } // <init>(short)
535
536        /** Copy constructor. */
537        protected Axis(Axis axis) {
538            type = axis.type;
539        } // <init>(Axis)
540
541        //
542        // Object methods
543        //
544
545        /** Returns a string representation of this object. */
546        public String toString() {
547            switch (type) {
548                case CHILD: return "child";
549                case ATTRIBUTE: return "attribute";
550                case SELF: return "self";
551                case DESCENDANT: return "descendant";
552            }
553            return "???";
554        } // toString():String
555
556        /** Returns a clone of this object. */
557        public Object clone() {
558            return new Axis(this);
559        } // clone():Object
560
561    } // class Axis
562
563    /**
564     * Node test.
565     *
566     * @xerces.internal
567     *
568     * @author Andy Clark, IBM
569     */
570    public static class NodeTest
571        implements Cloneable {
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 final 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 prefix, String 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 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 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     * @xerces.internal
681     *
682     * @author Glenn Marcy, IBM
683     * @author Andy Clark, IBM
684     *
685     */
686    private static final class Tokens {
687
688        static final boolean DUMP_TOKENS = false;
689
690        /**
691         * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
692         *                  | NameTest | NodeType | Operator | FunctionName
693         *                  | AxisName | Literal | Number | VariableReference
694         */
695        public static final int
696            EXPRTOKEN_OPEN_PAREN                    =   0,
697            EXPRTOKEN_CLOSE_PAREN                   =   1,
698            EXPRTOKEN_OPEN_BRACKET                  =   2,
699            EXPRTOKEN_CLOSE_BRACKET                 =   3,
700            EXPRTOKEN_PERIOD                        =   4,
701            EXPRTOKEN_DOUBLE_PERIOD                 =   5,
702            EXPRTOKEN_ATSIGN                        =   6,
703            EXPRTOKEN_COMMA                         =   7,
704            EXPRTOKEN_DOUBLE_COLON                  =   8,
705            //
706            // [37] NameTest ::= '*' | NCName ':' '*' | QName
707            //
708            // followed by symbol handle of NCName or QName
709            //
710            EXPRTOKEN_NAMETEST_ANY                  =   9,
711            EXPRTOKEN_NAMETEST_NAMESPACE            =  10,
712            EXPRTOKEN_NAMETEST_QNAME                =  11,
713            //
714            // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
715            //
716            EXPRTOKEN_NODETYPE_COMMENT              =  12,
717            EXPRTOKEN_NODETYPE_TEXT                 =  13,
718            EXPRTOKEN_NODETYPE_PI                   =  14,
719            EXPRTOKEN_NODETYPE_NODE                 =  15,
720            //
721            // [32] Operator ::= OperatorName
722            //                 | MultiplyOperator
723            //                 | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
724            // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
725            // [34] MultiplyOperator ::= '*'
726            //
727            EXPRTOKEN_OPERATOR_AND                  =  16,
728            EXPRTOKEN_OPERATOR_OR                   =  17,
729            EXPRTOKEN_OPERATOR_MOD                  =  18,
730            EXPRTOKEN_OPERATOR_DIV                  =  19,
731            EXPRTOKEN_OPERATOR_MULT                 =  20,
732            EXPRTOKEN_OPERATOR_SLASH                =  21,
733            EXPRTOKEN_OPERATOR_DOUBLE_SLASH         =  22,
734            EXPRTOKEN_OPERATOR_UNION                =  23,
735            EXPRTOKEN_OPERATOR_PLUS                 =  24,
736            EXPRTOKEN_OPERATOR_MINUS                =  25,
737            EXPRTOKEN_OPERATOR_EQUAL                =  26,
738            EXPRTOKEN_OPERATOR_NOT_EQUAL            =  27,
739            EXPRTOKEN_OPERATOR_LESS                 =  28,
740            EXPRTOKEN_OPERATOR_LESS_EQUAL           =  29,
741            EXPRTOKEN_OPERATOR_GREATER              =  30,
742            EXPRTOKEN_OPERATOR_GREATER_EQUAL        =  31,
743
744            //EXPRTOKEN_FIRST_OPERATOR                = EXPRTOKEN_OPERATOR_AND,
745            //EXPRTOKEN_LAST_OPERATOR                 = EXPRTOKEN_OPERATOR_GREATER_EQUAL,
746
747            //
748            // [35] FunctionName ::= QName - NodeType
749            //
750            // followed by symbol handle
751            //
752            EXPRTOKEN_FUNCTION_NAME                 =  32,
753            //
754            // [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
755            //                | 'attribute'
756            //                | 'child'
757            //                | 'descendant' | 'descendant-or-self'
758            //                | 'following' | 'following-sibling'
759            //                | 'namespace'
760            //                | 'parent'
761            //                | 'preceding' | 'preceding-sibling'
762            //                | 'self'
763            //
764            EXPRTOKEN_AXISNAME_ANCESTOR             =  33,
765            EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF     =  34,
766            EXPRTOKEN_AXISNAME_ATTRIBUTE            =  35,
767            EXPRTOKEN_AXISNAME_CHILD                =  36,
768            EXPRTOKEN_AXISNAME_DESCENDANT           =  37,
769            EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF   =  38,
770            EXPRTOKEN_AXISNAME_FOLLOWING            =  39,
771            EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING    =  40,
772            EXPRTOKEN_AXISNAME_NAMESPACE            =  41,
773            EXPRTOKEN_AXISNAME_PARENT               =  42,
774            EXPRTOKEN_AXISNAME_PRECEDING            =  43,
775            EXPRTOKEN_AXISNAME_PRECEDING_SIBLING    =  44,
776            EXPRTOKEN_AXISNAME_SELF                 =  45,
777            //
778            // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
779            //
780            // followed by symbol handle for literal
781            //
782            EXPRTOKEN_LITERAL                       =  46,
783            //
784            // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
785            // [31] Digits ::= [0-9]+
786            //
787            // followed by number handle
788            //
789            EXPRTOKEN_NUMBER                        =  47,
790            //
791            // [36] VariableReference ::= '$' QName
792            //
793            // followed by symbol handle for QName
794            //
795            EXPRTOKEN_VARIABLE_REFERENCE            =  48;
796
797        private static final String[] fgTokenNames = {
798            "EXPRTOKEN_OPEN_PAREN",
799            "EXPRTOKEN_CLOSE_PAREN",
800            "EXPRTOKEN_OPEN_BRACKET",
801            "EXPRTOKEN_CLOSE_BRACKET",
802            "EXPRTOKEN_PERIOD",
803            "EXPRTOKEN_DOUBLE_PERIOD",
804            "EXPRTOKEN_ATSIGN",
805            "EXPRTOKEN_COMMA",
806            "EXPRTOKEN_DOUBLE_COLON",
807            "EXPRTOKEN_NAMETEST_ANY",
808            "EXPRTOKEN_NAMETEST_NAMESPACE",
809            "EXPRTOKEN_NAMETEST_QNAME",
810            "EXPRTOKEN_NODETYPE_COMMENT",
811            "EXPRTOKEN_NODETYPE_TEXT",
812            "EXPRTOKEN_NODETYPE_PI",
813            "EXPRTOKEN_NODETYPE_NODE",
814            "EXPRTOKEN_OPERATOR_AND",
815            "EXPRTOKEN_OPERATOR_OR",
816            "EXPRTOKEN_OPERATOR_MOD",
817            "EXPRTOKEN_OPERATOR_DIV",
818            "EXPRTOKEN_OPERATOR_MULT",
819            "EXPRTOKEN_OPERATOR_SLASH",
820            "EXPRTOKEN_OPERATOR_DOUBLE_SLASH",
821            "EXPRTOKEN_OPERATOR_UNION",
822            "EXPRTOKEN_OPERATOR_PLUS",
823            "EXPRTOKEN_OPERATOR_MINUS",
824            "EXPRTOKEN_OPERATOR_EQUAL",
825            "EXPRTOKEN_OPERATOR_NOT_EQUAL",
826            "EXPRTOKEN_OPERATOR_LESS",
827            "EXPRTOKEN_OPERATOR_LESS_EQUAL",
828            "EXPRTOKEN_OPERATOR_GREATER",
829            "EXPRTOKEN_OPERATOR_GREATER_EQUAL",
830            "EXPRTOKEN_FUNCTION_NAME",
831            "EXPRTOKEN_AXISNAME_ANCESTOR",
832            "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF",
833            "EXPRTOKEN_AXISNAME_ATTRIBUTE",
834            "EXPRTOKEN_AXISNAME_CHILD",
835            "EXPRTOKEN_AXISNAME_DESCENDANT",
836            "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF",
837            "EXPRTOKEN_AXISNAME_FOLLOWING",
838            "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING",
839            "EXPRTOKEN_AXISNAME_NAMESPACE",
840            "EXPRTOKEN_AXISNAME_PARENT",
841            "EXPRTOKEN_AXISNAME_PRECEDING",
842            "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING",
843            "EXPRTOKEN_AXISNAME_SELF",
844            "EXPRTOKEN_LITERAL",
845            "EXPRTOKEN_NUMBER",
846            "EXPRTOKEN_VARIABLE_REFERENCE"
847        };
848
849        /**
850         *
851         */
852        private static final int INITIAL_TOKEN_COUNT = 1 << 8;
853        private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
854        private int fTokenCount = 0;    // for writing
855
856        private final SymbolTable fSymbolTable;
857
858        // REVISIT: Code something better here. -Ac
859        private final Map<String, Integer> fSymbolMapping = new HashMap<>();
860
861        // REVISIT: Code something better here. -Ac
862        private final Map<Integer, String> fTokenNames = new HashMap<>();
863
864        /**
865         * Current position in the token list.
866         */
867        private int fCurrentTokenIndex;
868
869        //
870        // Constructors
871        //
872
873        public Tokens(SymbolTable symbolTable) {
874            fSymbolTable = symbolTable;
875            final String[] symbols = {
876                "ancestor",     "ancestor-or-self",     "attribute",
877                "child",        "descendant",           "descendant-or-self",
878                "following",    "following-sibling",    "namespace",
879                "parent",       "preceding",            "preceding-sibling",
880                "self",
881            };
882            for (int i = 0; i < symbols.length; i++) {
883                fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]), i);
884            }
885            fTokenNames.put(EXPRTOKEN_OPEN_PAREN, "EXPRTOKEN_OPEN_PAREN");
886            fTokenNames.put(EXPRTOKEN_CLOSE_PAREN, "EXPRTOKEN_CLOSE_PAREN");
887            fTokenNames.put(EXPRTOKEN_OPEN_BRACKET, "EXPRTOKEN_OPEN_BRACKET");
888            fTokenNames.put(EXPRTOKEN_CLOSE_BRACKET, "EXPRTOKEN_CLOSE_BRACKET");
889            fTokenNames.put(EXPRTOKEN_PERIOD, "EXPRTOKEN_PERIOD");
890            fTokenNames.put(EXPRTOKEN_DOUBLE_PERIOD, "EXPRTOKEN_DOUBLE_PERIOD");
891            fTokenNames.put(EXPRTOKEN_ATSIGN, "EXPRTOKEN_ATSIGN");
892            fTokenNames.put(EXPRTOKEN_COMMA, "EXPRTOKEN_COMMA");
893            fTokenNames.put(EXPRTOKEN_DOUBLE_COLON, "EXPRTOKEN_DOUBLE_COLON");
894            fTokenNames.put(EXPRTOKEN_NAMETEST_ANY, "EXPRTOKEN_NAMETEST_ANY");
895            fTokenNames.put(EXPRTOKEN_NAMETEST_NAMESPACE, "EXPRTOKEN_NAMETEST_NAMESPACE");
896            fTokenNames.put(EXPRTOKEN_NAMETEST_QNAME, "EXPRTOKEN_NAMETEST_QNAME");
897            fTokenNames.put(EXPRTOKEN_NODETYPE_COMMENT, "EXPRTOKEN_NODETYPE_COMMENT");
898            fTokenNames.put(EXPRTOKEN_NODETYPE_TEXT, "EXPRTOKEN_NODETYPE_TEXT");
899            fTokenNames.put(EXPRTOKEN_NODETYPE_PI, "EXPRTOKEN_NODETYPE_PI");
900            fTokenNames.put(EXPRTOKEN_NODETYPE_NODE, "EXPRTOKEN_NODETYPE_NODE");
901            fTokenNames.put(EXPRTOKEN_OPERATOR_AND, "EXPRTOKEN_OPERATOR_AND");
902            fTokenNames.put(EXPRTOKEN_OPERATOR_OR, "EXPRTOKEN_OPERATOR_OR");
903            fTokenNames.put(EXPRTOKEN_OPERATOR_MOD, "EXPRTOKEN_OPERATOR_MOD");
904            fTokenNames.put(EXPRTOKEN_OPERATOR_DIV, "EXPRTOKEN_OPERATOR_DIV");
905            fTokenNames.put(EXPRTOKEN_OPERATOR_MULT, "EXPRTOKEN_OPERATOR_MULT");
906            fTokenNames.put(EXPRTOKEN_OPERATOR_SLASH, "EXPRTOKEN_OPERATOR_SLASH");
907            fTokenNames.put(EXPRTOKEN_OPERATOR_DOUBLE_SLASH, "EXPRTOKEN_OPERATOR_DOUBLE_SLASH");
908            fTokenNames.put(EXPRTOKEN_OPERATOR_UNION, "EXPRTOKEN_OPERATOR_UNION");
909            fTokenNames.put(EXPRTOKEN_OPERATOR_PLUS, "EXPRTOKEN_OPERATOR_PLUS");
910            fTokenNames.put(EXPRTOKEN_OPERATOR_MINUS, "EXPRTOKEN_OPERATOR_MINUS");
911            fTokenNames.put(EXPRTOKEN_OPERATOR_EQUAL, "EXPRTOKEN_OPERATOR_EQUAL");
912            fTokenNames.put(EXPRTOKEN_OPERATOR_NOT_EQUAL, "EXPRTOKEN_OPERATOR_NOT_EQUAL");
913            fTokenNames.put(EXPRTOKEN_OPERATOR_LESS, "EXPRTOKEN_OPERATOR_LESS");
914            fTokenNames.put(EXPRTOKEN_OPERATOR_LESS_EQUAL, "EXPRTOKEN_OPERATOR_LESS_EQUAL");
915            fTokenNames.put(EXPRTOKEN_OPERATOR_GREATER, "EXPRTOKEN_OPERATOR_GREATER");
916            fTokenNames.put(EXPRTOKEN_OPERATOR_GREATER_EQUAL, "EXPRTOKEN_OPERATOR_GREATER_EQUAL");
917            fTokenNames.put(EXPRTOKEN_FUNCTION_NAME, "EXPRTOKEN_FUNCTION_NAME");
918            fTokenNames.put(EXPRTOKEN_AXISNAME_ANCESTOR, "EXPRTOKEN_AXISNAME_ANCESTOR");
919            fTokenNames.put(EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF, "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF");
920            fTokenNames.put(EXPRTOKEN_AXISNAME_ATTRIBUTE, "EXPRTOKEN_AXISNAME_ATTRIBUTE");
921            fTokenNames.put(EXPRTOKEN_AXISNAME_CHILD, "EXPRTOKEN_AXISNAME_CHILD");
922            fTokenNames.put(EXPRTOKEN_AXISNAME_DESCENDANT, "EXPRTOKEN_AXISNAME_DESCENDANT");
923            fTokenNames.put(EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF, "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF");
924            fTokenNames.put(EXPRTOKEN_AXISNAME_FOLLOWING, "EXPRTOKEN_AXISNAME_FOLLOWING");
925            fTokenNames.put(EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING, "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING");
926            fTokenNames.put(EXPRTOKEN_AXISNAME_NAMESPACE, "EXPRTOKEN_AXISNAME_NAMESPACE");
927            fTokenNames.put(EXPRTOKEN_AXISNAME_PARENT, "EXPRTOKEN_AXISNAME_PARENT");
928            fTokenNames.put(EXPRTOKEN_AXISNAME_PRECEDING, "EXPRTOKEN_AXISNAME_PRECEDING");
929            fTokenNames.put(EXPRTOKEN_AXISNAME_PRECEDING_SIBLING, "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING");
930            fTokenNames.put(EXPRTOKEN_AXISNAME_SELF, "EXPRTOKEN_AXISNAME_SELF");
931            fTokenNames.put(EXPRTOKEN_LITERAL, "EXPRTOKEN_LITERAL");
932            fTokenNames.put(EXPRTOKEN_NUMBER, "EXPRTOKEN_NUMBER");
933            fTokenNames.put(EXPRTOKEN_VARIABLE_REFERENCE, "EXPRTOKEN_VARIABLE_REFERENCE");
934        }
935
936        //
937        // Public methods
938        //
939
940//        public String getTokenName(int token) {
941//            if (token < 0 || token >= fgTokenNames.length)
942//                return null;
943//            return fgTokenNames[token];
944//        }
945//
946        public String getTokenString(int token) {
947            return fTokenNames.get(token);
948        }
949
950        public void addToken(String tokenStr) {
951            Integer tokenInt = null;
952            for (Map.Entry<Integer, String> entry : fTokenNames.entrySet()) {
953                if (entry.getValue().equals(tokenStr)) {
954                    tokenInt = entry.getKey();
955                }
956            }
957            if (tokenInt == null) {
958                tokenInt = fTokenNames.size();
959                fTokenNames.put(tokenInt, tokenStr);
960            }
961            addToken(tokenInt);
962        }
963
964        public void addToken(int token) {
965            try {
966                fTokens[fTokenCount] = token;
967            } catch (ArrayIndexOutOfBoundsException ex) {
968                int[] oldList = fTokens;
969                fTokens = new int[fTokenCount << 1];
970                System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
971                fTokens[fTokenCount] = token;
972            }
973            fTokenCount++;
974        }
975//        public int getTokenCount() {
976//            return fTokenCount;
977//        }
978//        public int getToken(int tokenIndex) {
979//            return fTokens[tokenIndex];
980//        }
981
982        /**
983         * Resets the current position to the head of the token list.
984         */
985        public void rewind() {
986            fCurrentTokenIndex=0;
987        }
988        /**
989         * Returns true if the {@link #getNextToken()} method
990         * returns a valid token.
991         */
992        public boolean hasMore() {
993            return fCurrentTokenIndex<fTokenCount;
994        }
995        /**
996         * Obtains the token at the current position, then advance
997         * the current position by one.
998         *
999         * If there's no such next token, this method throws
1000         * <tt>new XPathException("c-general-xpath");</tt>.
1001         */
1002        public int nextToken() throws XPathException {
1003            if( fCurrentTokenIndex==fTokenCount )
1004                throw new XPathException("c-general-xpath");
1005            return fTokens[fCurrentTokenIndex++];
1006        }
1007        /**
1008         * Obtains the token at the current position, without advancing
1009         * the current position.
1010         *
1011         * If there's no such next token, this method throws
1012         * <tt>new XPathException("c-general-xpath");</tt>.
1013         */
1014        public int peekToken() throws XPathException {
1015            if( fCurrentTokenIndex==fTokenCount )
1016                throw new XPathException("c-general-xpath");
1017            return fTokens[fCurrentTokenIndex];
1018        }
1019        /**
1020         * Obtains the token at the current position as a String.
1021         *
1022         * If there's no current token or if the current token
1023         * is not a string token, this method throws
1024         * <tt>new XPathException("c-general-xpath");</tt>.
1025         */
1026        public String nextTokenAsString() throws XPathException {
1027            String s = getTokenString(nextToken());
1028            if(s==null)     throw new XPathException("c-general-xpath");
1029            return s;
1030        }
1031
1032        public void dumpTokens() {
1033            //if (DUMP_TOKENS) {
1034                for (int i = 0; i < fTokenCount; i++) {
1035                    switch (fTokens[i]) {
1036                    case EXPRTOKEN_OPEN_PAREN:
1037                        System.out.print("<OPEN_PAREN/>");
1038                        break;
1039                    case EXPRTOKEN_CLOSE_PAREN:
1040                        System.out.print("<CLOSE_PAREN/>");
1041                        break;
1042                    case EXPRTOKEN_OPEN_BRACKET:
1043                        System.out.print("<OPEN_BRACKET/>");
1044                        break;
1045                    case EXPRTOKEN_CLOSE_BRACKET:
1046                        System.out.print("<CLOSE_BRACKET/>");
1047                        break;
1048                    case EXPRTOKEN_PERIOD:
1049                        System.out.print("<PERIOD/>");
1050                        break;
1051                    case EXPRTOKEN_DOUBLE_PERIOD:
1052                        System.out.print("<DOUBLE_PERIOD/>");
1053                        break;
1054                    case EXPRTOKEN_ATSIGN:
1055                        System.out.print("<ATSIGN/>");
1056                        break;
1057                    case EXPRTOKEN_COMMA:
1058                        System.out.print("<COMMA/>");
1059                        break;
1060                    case EXPRTOKEN_DOUBLE_COLON:
1061                        System.out.print("<DOUBLE_COLON/>");
1062                        break;
1063                    case EXPRTOKEN_NAMETEST_ANY:
1064                        System.out.print("<NAMETEST_ANY/>");
1065                        break;
1066                    case EXPRTOKEN_NAMETEST_NAMESPACE:
1067                        System.out.print("<NAMETEST_NAMESPACE");
1068                        System.out.print(" prefix=\"" + getTokenString(fTokens[++i]) + "\"");
1069                        System.out.print("/>");
1070                        break;
1071                    case EXPRTOKEN_NAMETEST_QNAME:
1072                        System.out.print("<NAMETEST_QNAME");
1073                        if (fTokens[++i] != -1)
1074                            System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
1075                        System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
1076                        System.out.print("/>");
1077                        break;
1078                    case EXPRTOKEN_NODETYPE_COMMENT:
1079                        System.out.print("<NODETYPE_COMMENT/>");
1080                        break;
1081                    case EXPRTOKEN_NODETYPE_TEXT:
1082                        System.out.print("<NODETYPE_TEXT/>");
1083                        break;
1084                    case EXPRTOKEN_NODETYPE_PI:
1085                        System.out.print("<NODETYPE_PI/>");
1086                        break;
1087                    case EXPRTOKEN_NODETYPE_NODE:
1088                        System.out.print("<NODETYPE_NODE/>");
1089                        break;
1090                    case EXPRTOKEN_OPERATOR_AND:
1091                        System.out.print("<OPERATOR_AND/>");
1092                        break;
1093                    case EXPRTOKEN_OPERATOR_OR:
1094                        System.out.print("<OPERATOR_OR/>");
1095                        break;
1096                    case EXPRTOKEN_OPERATOR_MOD:
1097                        System.out.print("<OPERATOR_MOD/>");
1098                        break;
1099                    case EXPRTOKEN_OPERATOR_DIV:
1100                        System.out.print("<OPERATOR_DIV/>");
1101                        break;
1102                    case EXPRTOKEN_OPERATOR_MULT:
1103                        System.out.print("<OPERATOR_MULT/>");
1104                        break;
1105                    case EXPRTOKEN_OPERATOR_SLASH:
1106                        System.out.print("<OPERATOR_SLASH/>");
1107                        if (i + 1 < fTokenCount) {
1108                            System.out.println();
1109                            System.out.print("  ");
1110                        }
1111                        break;
1112                    case EXPRTOKEN_OPERATOR_DOUBLE_SLASH:
1113                        System.out.print("<OPERATOR_DOUBLE_SLASH/>");
1114                        break;
1115                    case EXPRTOKEN_OPERATOR_UNION:
1116                        System.out.print("<OPERATOR_UNION/>");
1117                        break;
1118                    case EXPRTOKEN_OPERATOR_PLUS:
1119                        System.out.print("<OPERATOR_PLUS/>");
1120                        break;
1121                    case EXPRTOKEN_OPERATOR_MINUS:
1122                        System.out.print("<OPERATOR_MINUS/>");
1123                        break;
1124                    case EXPRTOKEN_OPERATOR_EQUAL:
1125                        System.out.print("<OPERATOR_EQUAL/>");
1126                        break;
1127                    case EXPRTOKEN_OPERATOR_NOT_EQUAL:
1128                        System.out.print("<OPERATOR_NOT_EQUAL/>");
1129                        break;
1130                    case EXPRTOKEN_OPERATOR_LESS:
1131                        System.out.print("<OPERATOR_LESS/>");
1132                        break;
1133                    case EXPRTOKEN_OPERATOR_LESS_EQUAL:
1134                        System.out.print("<OPERATOR_LESS_EQUAL/>");
1135                        break;
1136                    case EXPRTOKEN_OPERATOR_GREATER:
1137                        System.out.print("<OPERATOR_GREATER/>");
1138                        break;
1139                    case EXPRTOKEN_OPERATOR_GREATER_EQUAL:
1140                        System.out.print("<OPERATOR_GREATER_EQUAL/>");
1141                        break;
1142                    case EXPRTOKEN_FUNCTION_NAME:
1143                        System.out.print("<FUNCTION_NAME");
1144                        if (fTokens[++i] != -1)
1145                            System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
1146                        System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
1147                        System.out.print("/>");
1148                        break;
1149                    case EXPRTOKEN_AXISNAME_ANCESTOR:
1150                        System.out.print("<AXISNAME_ANCESTOR/>");
1151                        break;
1152                    case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
1153                        System.out.print("<AXISNAME_ANCESTOR_OR_SELF/>");
1154                        break;
1155                    case EXPRTOKEN_AXISNAME_ATTRIBUTE:
1156                        System.out.print("<AXISNAME_ATTRIBUTE/>");
1157                        break;
1158                    case EXPRTOKEN_AXISNAME_CHILD:
1159                        System.out.print("<AXISNAME_CHILD/>");
1160                        break;
1161                    case EXPRTOKEN_AXISNAME_DESCENDANT:
1162                        System.out.print("<AXISNAME_DESCENDANT/>");
1163                        break;
1164                    case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
1165                        System.out.print("<AXISNAME_DESCENDANT_OR_SELF/>");
1166                        break;
1167                    case EXPRTOKEN_AXISNAME_FOLLOWING:
1168                        System.out.print("<AXISNAME_FOLLOWING/>");
1169                        break;
1170                    case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
1171                        System.out.print("<AXISNAME_FOLLOWING_SIBLING/>");
1172                        break;
1173                    case EXPRTOKEN_AXISNAME_NAMESPACE:
1174                        System.out.print("<AXISNAME_NAMESPACE/>");
1175                        break;
1176                    case EXPRTOKEN_AXISNAME_PARENT:
1177                        System.out.print("<AXISNAME_PARENT/>");
1178                        break;
1179                    case EXPRTOKEN_AXISNAME_PRECEDING:
1180                        System.out.print("<AXISNAME_PRECEDING/>");
1181                        break;
1182                    case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
1183                        System.out.print("<AXISNAME_PRECEDING_SIBLING/>");
1184                        break;
1185                    case EXPRTOKEN_AXISNAME_SELF:
1186                        System.out.print("<AXISNAME_SELF/>");
1187                        break;
1188                    case EXPRTOKEN_LITERAL:
1189                        System.out.print("<LITERAL");
1190                        System.out.print(" value=\"" + getTokenString(fTokens[++i]) + "\"");
1191                        System.out.print("/>");
1192                        break;
1193                    case EXPRTOKEN_NUMBER:
1194                        System.out.print("<NUMBER");
1195                        System.out.print(" whole=\"" + getTokenString(fTokens[++i]) + "\"");
1196                        System.out.print(" part=\"" + getTokenString(fTokens[++i]) + "\"");
1197                        System.out.print("/>");
1198                        break;
1199                    case EXPRTOKEN_VARIABLE_REFERENCE:
1200                        System.out.print("<VARIABLE_REFERENCE");
1201                        if (fTokens[++i] != -1)
1202                            System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
1203                        System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
1204                        System.out.print("/>");
1205                        break;
1206                    default:
1207                        System.out.println("<???/>");
1208                    }
1209                }
1210                System.out.println();
1211            //}
1212        }
1213
1214    } // class Tokens
1215
1216    /**
1217     * @xerces.internal
1218     *
1219     * @author Glenn Marcy, IBM
1220     * @author Andy Clark, IBM
1221     *
1222     */
1223    private static class Scanner {
1224
1225        /**
1226         * 7-bit ASCII subset
1227         *
1228         *  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
1229         *  0,  0,  0,  0,  0,  0,  0,  0,  0, HT, LF,  0,  0, CR,  0,  0,  // 0
1230         *  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
1231         * SP,  !,  ",  #,  $,  %,  &,  ',  (,  ),  *,  +,  ,,  -,  .,  /,  // 2
1232         *  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  :,  ;,  <,  =,  >,  ?,  // 3
1233         *  @,  A,  B,  C,  D,  E,  F,  G,  H,  I,  J,  K,  L,  M,  N,  O,  // 4
1234         *  P,  Q,  R,  S,  T,  U,  V,  W,  X,  Y,  Z,  [,  \,  ],  ^,  _,  // 5
1235         *  `,  a,  b,  c,  d,  e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  // 6
1236         *  p,  q,  r,  s,  t,  u,  v,  w,  x,  y,  z,  {,  |,  },  ~, DEL  // 7
1237         */
1238        private static final byte
1239            CHARTYPE_INVALID            =  0,   // invalid XML character
1240            CHARTYPE_OTHER              =  1,   // not special - one of "#%&;?\^`{}~" or DEL
1241            CHARTYPE_WHITESPACE         =  2,   // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
1242            CHARTYPE_EXCLAMATION        =  3,   // '!' (0x21)
1243            CHARTYPE_QUOTE              =  4,   // '\"' or '\'' (0x22 and 0x27)
1244            CHARTYPE_DOLLAR             =  5,   // '$' (0x24)
1245            CHARTYPE_OPEN_PAREN         =  6,   // '(' (0x28)
1246            CHARTYPE_CLOSE_PAREN        =  7,   // ')' (0x29)
1247            CHARTYPE_STAR               =  8,   // '*' (0x2A)
1248            CHARTYPE_PLUS               =  9,   // '+' (0x2B)
1249            CHARTYPE_COMMA              = 10,   // ',' (0x2C)
1250            CHARTYPE_MINUS              = 11,   // '-' (0x2D)
1251            CHARTYPE_PERIOD             = 12,   // '.' (0x2E)
1252            CHARTYPE_SLASH              = 13,   // '/' (0x2F)
1253            CHARTYPE_DIGIT              = 14,   // '0'-'9' (0x30 to 0x39)
1254            CHARTYPE_COLON              = 15,   // ':' (0x3A)
1255            CHARTYPE_LESS               = 16,   // '<' (0x3C)
1256            CHARTYPE_EQUAL              = 17,   // '=' (0x3D)
1257            CHARTYPE_GREATER            = 18,   // '>' (0x3E)
1258            CHARTYPE_ATSIGN             = 19,   // '@' (0x40)
1259            CHARTYPE_LETTER             = 20,   // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
1260            CHARTYPE_OPEN_BRACKET       = 21,   // '[' (0x5B)
1261            CHARTYPE_CLOSE_BRACKET      = 22,   // ']' (0x5D)
1262            CHARTYPE_UNDERSCORE         = 23,   // '_' (0x5F)
1263            CHARTYPE_UNION              = 24,   // '|' (0x7C)
1264            CHARTYPE_NONASCII           = 25;   // Non-ASCII Unicode codepoint (>= 0x80)
1265
1266        private static final byte[] fASCIICharMap = {
1267            0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  2,  0,  0,  2,  0,  0,
1268            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1269            2,  3,  4,  1,  5,  1,  1,  4,  6,  7,  8,  9, 10, 11, 12, 13,
1270           14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,  1, 16, 17, 18,  1,
1271           19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
1272           20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,  1, 22,  1, 23,
1273            1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
1274           20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,  1, 24,  1,  1,  1
1275        };
1276
1277        /**
1278         * Symbol literals
1279         */
1280
1281        //
1282        // Data
1283        //
1284
1285        /** Symbol table. */
1286        private SymbolTable fSymbolTable;
1287
1288        // symbols
1289
1290        private static final String fAndSymbol = "and".intern();
1291        private static final String fOrSymbol = "or".intern();
1292        private static final String fModSymbol = "mod".intern();
1293        private static final String fDivSymbol = "div".intern();
1294
1295        private static final String fCommentSymbol = "comment".intern();
1296        private static final String fTextSymbol = "text".intern();
1297        private static final String fPISymbol = "processing-instruction".intern();
1298        private static final String fNodeSymbol = "node".intern();
1299
1300        private static final String fAncestorSymbol = "ancestor".intern();
1301        private static final String fAncestorOrSelfSymbol = "ancestor-or-self".intern();
1302        private static final String fAttributeSymbol = "attribute".intern();
1303        private static final String fChildSymbol = "child".intern();
1304        private static final String fDescendantSymbol = "descendant".intern();
1305        private static final String fDescendantOrSelfSymbol = "descendant-or-self".intern();
1306        private static final String fFollowingSymbol = "following".intern();
1307        private static final String fFollowingSiblingSymbol = "following-sibling".intern();
1308        private static final String fNamespaceSymbol = "namespace".intern();
1309        private static final String fParentSymbol = "parent".intern();
1310        private static final String fPrecedingSymbol = "preceding".intern();
1311        private static final String fPrecedingSiblingSymbol = "preceding-sibling".intern();
1312        private static final String fSelfSymbol = "self".intern();
1313
1314        //
1315        // Constructors
1316        //
1317
1318        /** Constructs an XPath expression scanner. */
1319        public Scanner(SymbolTable symbolTable) {
1320
1321            // save pool and tokens
1322            fSymbolTable = symbolTable;
1323
1324        } // <init>(SymbolTable)
1325
1326        /**
1327         *
1328         */
1329        public boolean scanExpr(SymbolTable symbolTable,
1330                                XPath.Tokens tokens, String data,
1331                                int currentOffset, int endOffset)
1332            throws XPathException {
1333
1334            int nameOffset;
1335            String nameHandle, prefixHandle;
1336            boolean starIsMultiplyOperator = false;
1337            int ch;
1338
1339            while (true) {
1340                if (currentOffset == endOffset) {
1341                    break;
1342                }
1343                ch = data.charAt(currentOffset);
1344                //
1345                // [39] ExprWhitespace ::= S
1346                //
1347                while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1348                    if (++currentOffset == endOffset) {
1349                        break;
1350                    }
1351                    ch = data.charAt(currentOffset);
1352                }
1353                if (currentOffset == endOffset) {
1354                    break;
1355                }
1356                //
1357                // [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
1358                //                  | NameTest | NodeType | Operator | FunctionName
1359                //                  | AxisName | Literal | Number | VariableReference
1360                //
1361                byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch];
1362                switch (chartype) {
1363                case CHARTYPE_OPEN_PAREN:       // '('
1364                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1365                    starIsMultiplyOperator = false;
1366                    if (++currentOffset == endOffset) {
1367                        break;
1368                    }
1369                    break;
1370                case CHARTYPE_CLOSE_PAREN:      // ')'
1371                    addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN);
1372                    starIsMultiplyOperator = true;
1373                    if (++currentOffset == endOffset) {
1374                        break;
1375                    }
1376                    break;
1377                case CHARTYPE_OPEN_BRACKET:     // '['
1378                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_BRACKET);
1379                    starIsMultiplyOperator = false;
1380                    if (++currentOffset == endOffset) {
1381                        break;
1382                    }
1383                    break;
1384                case CHARTYPE_CLOSE_BRACKET:    // ']'
1385                    addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET);
1386                    starIsMultiplyOperator = true;
1387                    if (++currentOffset == endOffset) {
1388                        break;
1389                    }
1390                    break;
1391                //
1392                // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1393                //                                         ^^^^^^^^^^
1394                //
1395                case CHARTYPE_PERIOD:           // '.', '..' or '.' Digits
1396                    if (currentOffset + 1 == endOffset) {
1397                        addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1398                        starIsMultiplyOperator = true;
1399                        currentOffset++;
1400                        break;
1401                    }
1402                    ch = data.charAt(currentOffset + 1);
1403                    if (ch == '.') {            // '..'
1404                        addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD);
1405                        starIsMultiplyOperator = true;
1406                        currentOffset += 2;
1407                    } else if (ch >= '0' && ch <= '9') {
1408                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1409                        starIsMultiplyOperator = true;
1410                        currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
1411                    } else if (ch == '/') {
1412                        addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1413                        starIsMultiplyOperator = true;
1414                        currentOffset++;
1415                    } else if (ch == '|') {
1416                        addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1417                        starIsMultiplyOperator = true;
1418                        currentOffset++;
1419                        break;
1420                    } else if (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1421                        // this is legal if the next token is non-existent or |
1422                        do {
1423                            if (++currentOffset == endOffset) {
1424                                break;
1425                            }
1426                            ch = data.charAt(currentOffset);
1427                        } while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D);
1428                        if (currentOffset == endOffset || ch == '|' || ch == '/') {
1429                            addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1430                            starIsMultiplyOperator = true;
1431                            break;
1432                        }
1433                        throw new XPathException ("c-general-xpath");
1434                    } else {                    // '.'
1435                        throw new XPathException ("c-general-xpath");
1436                    }
1437                    if (currentOffset == endOffset) {
1438                        break;
1439                    }
1440                    break;
1441                case CHARTYPE_ATSIGN:           // '@'
1442                    addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN);
1443                    starIsMultiplyOperator = false;
1444                    if (++currentOffset == endOffset) {
1445                        break;
1446                    }
1447                    break;
1448                case CHARTYPE_COMMA:            // ','
1449                    addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA);
1450                    starIsMultiplyOperator = false;
1451                    if (++currentOffset == endOffset) {
1452                        break;
1453                    }
1454                    break;
1455                case CHARTYPE_COLON:            // '::'
1456                    if (++currentOffset == endOffset) {
1457                // System.out.println("abort 1a");
1458                        return false; // REVISIT
1459                    }
1460                    ch = data.charAt(currentOffset);
1461                    if (ch != ':') {
1462                // System.out.println("abort 1b");
1463                        return false; // REVISIT
1464                    }
1465                    addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1466                    starIsMultiplyOperator = false;
1467                    if (++currentOffset == endOffset) {
1468                        break;
1469                    }
1470                    break;
1471                case CHARTYPE_SLASH:            // '/' and '//'
1472                    if (++currentOffset == endOffset) {
1473                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1474                        starIsMultiplyOperator = false;
1475                        break;
1476                    }
1477                    ch = data.charAt(currentOffset);
1478                    if (ch == '/') { // '//'
1479                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH);
1480                        starIsMultiplyOperator = false;
1481                        if (++currentOffset == endOffset) {
1482                            break;
1483                        }
1484                    } else {
1485                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1486                        starIsMultiplyOperator = false;
1487                    }
1488                    break;
1489                case CHARTYPE_UNION:            // '|'
1490                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_UNION);
1491                    starIsMultiplyOperator = false;
1492                    if (++currentOffset == endOffset) {
1493                        break;
1494                    }
1495                    break;
1496                case CHARTYPE_PLUS:             // '+'
1497                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS);
1498                    starIsMultiplyOperator = false;
1499                    if (++currentOffset == endOffset) {
1500                        break;
1501                    }
1502                    break;
1503                case CHARTYPE_MINUS:            // '-'
1504                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS);
1505                    starIsMultiplyOperator = false;
1506                    if (++currentOffset == endOffset) {
1507                        break;
1508                    }
1509                    break;
1510                case CHARTYPE_EQUAL:            // '='
1511                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL);
1512                    starIsMultiplyOperator = false;
1513                    if (++currentOffset == endOffset) {
1514                        break;
1515                    }
1516                    break;
1517                case CHARTYPE_EXCLAMATION:      // '!='
1518                    if (++currentOffset == endOffset) {
1519                // System.out.println("abort 2a");
1520                        return false; // REVISIT
1521                    }
1522                    ch = data.charAt(currentOffset);
1523                    if (ch != '=') {
1524                // System.out.println("abort 2b");
1525                        return false; // REVISIT
1526                    }
1527                    addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL);
1528                    starIsMultiplyOperator = false;
1529                    if (++currentOffset == endOffset) {
1530                        break;
1531                    }
1532                    break;
1533                case CHARTYPE_LESS: // '<' and '<='
1534                    if (++currentOffset == endOffset) {
1535                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1536                        starIsMultiplyOperator = false;
1537                        break;
1538                    }
1539                    ch = data.charAt(currentOffset);
1540                    if (ch == '=') { // '<='
1541                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL);
1542                        starIsMultiplyOperator = false;
1543                        if (++currentOffset == endOffset) {
1544                            break;
1545                        }
1546                    } else {
1547                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1548                        starIsMultiplyOperator = false;
1549                    }
1550                    break;
1551                case CHARTYPE_GREATER: // '>' and '>='
1552                    if (++currentOffset == endOffset) {
1553                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1554                        starIsMultiplyOperator = false;
1555                        break;
1556                    }
1557                    ch = data.charAt(currentOffset);
1558                    if (ch == '=') { // '>='
1559                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL);
1560                        starIsMultiplyOperator = false;
1561                        if (++currentOffset == endOffset) {
1562                            break;
1563                        }
1564                    } else {
1565                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1566                        starIsMultiplyOperator = false;
1567                    }
1568                    break;
1569                //
1570                // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
1571                //
1572                case CHARTYPE_QUOTE:            // '\"' or '\''
1573                    int qchar = ch;
1574                    if (++currentOffset == endOffset) {
1575                // System.out.println("abort 2c");
1576                        return false; // REVISIT
1577                    }
1578                    ch = data.charAt(currentOffset);
1579                    int litOffset = currentOffset;
1580                    while (ch != qchar) {
1581                        if (++currentOffset == endOffset) {
1582                // System.out.println("abort 2d");
1583                            return false; // REVISIT
1584                        }
1585                        ch = data.charAt(currentOffset);
1586                    }
1587                    int litLength = currentOffset - litOffset;
1588                    addToken(tokens, XPath.Tokens.EXPRTOKEN_LITERAL);
1589                    starIsMultiplyOperator = true;
1590                    tokens.addToken(symbolTable.addSymbol(data.substring(litOffset, litOffset + litLength)));
1591                    if (++currentOffset == endOffset) {
1592                        break;
1593                    }
1594                    break;
1595                //
1596                // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1597                // [31] Digits ::= [0-9]+
1598                //
1599                case CHARTYPE_DIGIT:
1600                    addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1601                    starIsMultiplyOperator = true;
1602                    currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
1603                    break;
1604                //
1605                // [36] VariableReference ::= '$' QName
1606                //
1607                case CHARTYPE_DOLLAR:
1608                    if (++currentOffset == endOffset) {
1609                // System.out.println("abort 3a");
1610                        return false; // REVISIT
1611                    }
1612                    nameOffset = currentOffset;
1613                    currentOffset = scanNCName(data, endOffset, currentOffset);
1614                    if (currentOffset == nameOffset) {
1615                // System.out.println("abort 3b");
1616                        return false; // REVISIT
1617                    }
1618                    if (currentOffset < endOffset) {
1619                        ch = data.charAt(currentOffset);
1620                    }
1621                    else {
1622                        ch = -1;
1623                    }
1624                    nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1625                    if (ch != ':') {
1626                        prefixHandle = XMLSymbols.EMPTY_STRING;
1627                    } else {
1628                        prefixHandle = nameHandle;
1629                        if (++currentOffset == endOffset) {
1630                // System.out.println("abort 4a");
1631                            return false; // REVISIT
1632                        }
1633                        nameOffset = currentOffset;
1634                        currentOffset = scanNCName(data, endOffset, currentOffset);
1635                        if (currentOffset == nameOffset) {
1636                // System.out.println("abort 4b");
1637                            return false; // REVISIT
1638                        }
1639                        if (currentOffset < endOffset) {
1640                            ch = data.charAt(currentOffset);
1641                        }
1642                        else {
1643                            ch = -1;
1644                        }
1645                        nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1646                    }
1647                    addToken(tokens, XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE);
1648                    starIsMultiplyOperator = true;
1649                    tokens.addToken(prefixHandle);
1650                    tokens.addToken(nameHandle);
1651                    break;
1652                //
1653                // [37] NameTest ::= '*' | NCName ':' '*' | QName
1654                // [34] MultiplyOperator ::= '*'
1655                //
1656                case CHARTYPE_STAR:             // '*'
1657                    //
1658                    // 3.7 Lexical Structure
1659                    //
1660                    //  If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1661                    //  an Operator, then a * must be recognized as a MultiplyOperator.
1662                    //
1663                    // Otherwise, the token must not be recognized as a MultiplyOperator.
1664                    //
1665                    if (starIsMultiplyOperator) {
1666                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MULT);
1667                        starIsMultiplyOperator = false;
1668                    } else {
1669                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_ANY);
1670                        starIsMultiplyOperator = true;
1671                    }
1672                    if (++currentOffset == endOffset) {
1673                        break;
1674                    }
1675                    break;
1676                //
1677                // NCName, QName and non-terminals
1678                //
1679                case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic)
1680                case CHARTYPE_LETTER:
1681                case CHARTYPE_UNDERSCORE:
1682                    //
1683                    // 3.7 Lexical Structure
1684                    //
1685                    //  If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1686                    //  an Operator, then an NCName must be recognized as an OperatorName.
1687                    //
1688                    //  If the character following an NCName (possibly after intervening ExprWhitespace) is (,
1689                    //  then the token must be recognized as a NodeType or a FunctionName.
1690                    //
1691                    //  If the two characters following an NCName (possibly after intervening ExprWhitespace)
1692                    //  are ::, then the token must be recognized as an AxisName.
1693                    //
1694                    //  Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1695                    //  FunctionName, or an AxisName.
1696                    //
1697                    // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
1698                    // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
1699                    // [35] FunctionName ::= QName - NodeType
1700                    // [6] AxisName ::= (see above)
1701                    //
1702                    // [37] NameTest ::= '*' | NCName ':' '*' | QName
1703                    // [5] NCName ::= (Letter | '_') (NCNameChar)*
1704                    // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_'  (ascii subset of 'NCNameChar')
1705                    // [?] QName ::= (NCName ':')? NCName
1706                    // [?] Letter ::= [A-Za-z]                              (ascii subset of 'Letter')
1707                    // [?] Digit ::= [0-9]                                  (ascii subset of 'Digit')
1708                    //
1709                    nameOffset = currentOffset;
1710                    currentOffset = scanNCName(data, endOffset, currentOffset);
1711                    if (currentOffset == nameOffset) {
1712                // System.out.println("abort 4c");
1713                        return false; // REVISIT
1714                    }
1715                    if (currentOffset < endOffset) {
1716                        ch = data.charAt(currentOffset);
1717                    }
1718                    else {
1719                        ch = -1;
1720                    }
1721                    nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1722                    boolean isNameTestNCName = false;
1723                    boolean isAxisName = false;
1724                    prefixHandle = XMLSymbols.EMPTY_STRING;
1725                    if (ch == ':') {
1726                        if (++currentOffset == endOffset) {
1727                // System.out.println("abort 5");
1728                            return false; // REVISIT
1729                        }
1730                        ch = data.charAt(currentOffset);
1731                        if (ch == '*') {
1732                            if (++currentOffset < endOffset) {
1733                                ch = data.charAt(currentOffset);
1734                            }
1735                            isNameTestNCName = true;
1736                        } else if (ch == ':') {
1737                            if (++currentOffset < endOffset) {
1738                                ch = data.charAt(currentOffset);
1739                            }
1740                            isAxisName = true;
1741                        } else {
1742                            prefixHandle = nameHandle;
1743                            nameOffset = currentOffset;
1744                            currentOffset = scanNCName(data, endOffset, currentOffset);
1745                            if (currentOffset == nameOffset) {
1746                // System.out.println("abort 5b");
1747                                return false; // REVISIT
1748                            }
1749                            if (currentOffset < endOffset) {
1750                                ch = data.charAt(currentOffset);
1751                            }
1752                            else {
1753                                ch = -1;
1754                            }
1755                            nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1756                        }
1757                    }
1758                    //
1759                    // [39] ExprWhitespace ::= S
1760                    //
1761                    while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1762                        if (++currentOffset == endOffset) {
1763                            break;
1764                        }
1765                        ch = data.charAt(currentOffset);
1766                    }
1767                    //
1768                    //  If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1769                    //  an Operator, then an NCName must be recognized as an OperatorName.
1770                    //
1771                    if (starIsMultiplyOperator) {
1772                        if (nameHandle == fAndSymbol) {
1773                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_AND);
1774                            starIsMultiplyOperator = false;
1775                        } else if (nameHandle == fOrSymbol) {
1776                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_OR);
1777                            starIsMultiplyOperator = false;
1778                        } else if (nameHandle == fModSymbol) {
1779                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MOD);
1780                            starIsMultiplyOperator = false;
1781                        } else if (nameHandle == fDivSymbol) {
1782                            addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DIV);
1783                            starIsMultiplyOperator = false;
1784                        } else {
1785                // System.out.println("abort 6");
1786                            return false; // REVISIT
1787                        }
1788                        if (isNameTestNCName) {
1789                // System.out.println("abort 7");
1790                            return false; // REVISIT - NCName:* where an OperatorName is required
1791                        } else if (isAxisName) {
1792                // System.out.println("abort 8");
1793                            return false; // REVISIT - AxisName:: where an OperatorName is required
1794                        }
1795                        break;
1796                    }
1797                    //
1798                    //  If the character following an NCName (possibly after intervening ExprWhitespace) is (,
1799                    //  then the token must be recognized as a NodeType or a FunctionName.
1800                    //
1801                    if (ch == '(' && !isNameTestNCName && !isAxisName) {
1802                        if (nameHandle == fCommentSymbol) {
1803                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT);
1804                        } else if (nameHandle == fTextSymbol) {
1805                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT);
1806                        } else if (nameHandle == fPISymbol) {
1807                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_PI);
1808                        } else if (nameHandle == fNodeSymbol) {
1809                            addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_NODE);
1810                        } else {
1811                            addToken(tokens, XPath.Tokens.EXPRTOKEN_FUNCTION_NAME);
1812                            tokens.addToken(prefixHandle);
1813                            tokens.addToken(nameHandle);
1814                        }
1815                        addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1816                        starIsMultiplyOperator = false;
1817                        if (++currentOffset == endOffset) {
1818                            break;
1819                        }
1820                        break;
1821                    }
1822                    //
1823                    //  If the two characters following an NCName (possibly after intervening ExprWhitespace)
1824                    //  are ::, then the token must be recognized as an AxisName.
1825                    //
1826                    if (isAxisName ||
1827                        (ch == ':' && currentOffset + 1 < endOffset &&
1828                         data.charAt(currentOffset + 1) == ':')) {
1829                        if (nameHandle == fAncestorSymbol) {
1830                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR);
1831                        } else if (nameHandle == fAncestorOrSelfSymbol) {
1832                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF);
1833                        } else if (nameHandle == fAttributeSymbol) {
1834                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE);
1835                        } else if (nameHandle == fChildSymbol) {
1836                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD);
1837                        } else if (nameHandle == fDescendantSymbol) {
1838                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT);
1839                        } else if (nameHandle == fDescendantOrSelfSymbol) {
1840                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF);
1841                        } else if (nameHandle == fFollowingSymbol) {
1842                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING);
1843                        } else if (nameHandle == fFollowingSiblingSymbol) {
1844                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING);
1845                        } else if (nameHandle == fNamespaceSymbol) {
1846                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE);
1847                        } else if (nameHandle == fParentSymbol) {
1848                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT);
1849                        } else if (nameHandle == fPrecedingSymbol) {
1850                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING);
1851                        } else if (nameHandle == fPrecedingSiblingSymbol) {
1852                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING);
1853                        } else if (nameHandle == fSelfSymbol) {
1854                            addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_SELF);
1855                        } else {
1856                // System.out.println("abort 9");
1857                            return false; // REVISIT
1858                        }
1859                        if (isNameTestNCName) {
1860                // System.out.println("abort 10");
1861                            return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required
1862                        }
1863                        addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1864                        starIsMultiplyOperator = false;
1865                        if (!isAxisName) {
1866                            currentOffset++;
1867                            if (++currentOffset == endOffset) {
1868                                break;
1869                            }
1870                        }
1871                        break;
1872                    }
1873                    //
1874                    //  Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1875                    //  FunctionName, or an AxisName.
1876                    //
1877                    if (isNameTestNCName) {
1878                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE);
1879                        starIsMultiplyOperator = true;
1880                        tokens.addToken(nameHandle);
1881                    } else {
1882                        addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME);
1883                        starIsMultiplyOperator = true;
1884                        tokens.addToken(prefixHandle);
1885                        tokens.addToken(nameHandle);
1886                    }
1887                    break;
1888                default:
1889                    // CHARTYPE_INVALID or CHARTYPE_OTHER
1890                    // We're not expecting to find either of these in a valid expression.
1891                    return false;
1892                }
1893            }
1894            if (XPath.Tokens.DUMP_TOKENS) {
1895                tokens.dumpTokens();
1896            }
1897            return true;
1898        }
1899        //
1900        // [5] NCName ::= (Letter | '_') (NCNameChar)*
1901        // [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
1902        //
1903        int scanNCName(String data, int endOffset, int currentOffset) {
1904            int ch = data.charAt(currentOffset);
1905            if (ch >= 0x80) {
1906                if (!XMLChar.isNameStart(ch))
1907                /*** // REVISIT: Make sure this is a negation. ***
1908                if ((XMLCharacterProperties.fgCharFlags[ch] &
1909                     XMLCharacterProperties.E_InitialNameCharFlag) == 0)
1910                /***/
1911                {
1912                    return currentOffset;
1913                }
1914            }
1915            else {
1916                byte chartype = fASCIICharMap[ch];
1917                if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
1918                    return currentOffset;
1919                }
1920            }
1921            while (++currentOffset < endOffset) {
1922                ch = data.charAt(currentOffset);
1923                if (ch >= 0x80) {
1924                    if (!XMLChar.isName(ch))
1925                    /*** // REVISIT: Make sure this is a negation. ***
1926                    if ((XMLCharacterProperties.fgCharFlags[ch] &
1927                         XMLCharacterProperties.E_NameCharFlag) == 0)
1928                    /***/
1929                    {
1930                        break;
1931                    }
1932                }
1933                else {
1934                    byte chartype = fASCIICharMap[ch];
1935                    if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
1936                        chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
1937                        chartype != CHARTYPE_UNDERSCORE)
1938                    {
1939                        break;
1940                    }
1941                }
1942            }
1943            return currentOffset;
1944        }
1945        //
1946        // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1947        // [31] Digits ::= [0-9]+
1948        //
1949        private int scanNumber(XPath.Tokens tokens, String/*byte[]*/ data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) {
1950            int ch = data.charAt(currentOffset);
1951            int whole = 0;
1952            int part = 0;
1953            while (ch >= '0' && ch <= '9') {
1954                whole = (whole * 10) + (ch - '0');
1955                if (++currentOffset == endOffset) {
1956                    break;
1957                }
1958                ch = data.charAt(currentOffset);
1959            }
1960            if (ch == '.') {
1961                if (++currentOffset < endOffset) {
1962                    /** int start = currentOffset; **/
1963                    ch = data.charAt(currentOffset);
1964                    while (ch >= '0' && ch <= '9') {
1965                        part = (part * 10) + (ch - '0');
1966                        if (++currentOffset == endOffset) {
1967                            break;
1968                        }
1969                        ch = data.charAt(currentOffset);
1970                    }
1971                    if (part != 0) {
1972                        /***
1973                        part = tokens.addSymbol(data, start, currentOffset - start, encoding);
1974                        /***/
1975                        throw new RuntimeException("find a solution!");
1976                        //part = fStringPool.addSymbol(data.substring(start, currentOffset));
1977                        /***/
1978                    }
1979                }
1980            }
1981            tokens.addToken(whole);
1982            tokens.addToken(part);
1983            return currentOffset;
1984        }
1985
1986        //
1987        // Protected methods
1988        //
1989
1990        /**
1991         * This method adds the specified token to the token list. By
1992         * default, this method allows all tokens. However, subclasses
1993         * of the XPathExprScanner can override this method in order
1994         * to disallow certain tokens from being used in the scanned
1995         * XPath expression. This is a convenient way of allowing only
1996         * a subset of XPath.
1997         */
1998        protected void addToken(XPath.Tokens tokens, int token)
1999            throws XPathException {
2000            tokens.addToken(token);
2001        } // addToken(int)
2002
2003    } // class Scanner
2004
2005    //
2006    // MAIN
2007    //
2008
2009    /** Main program entry. */
2010    public static void main(String[] argv) throws Exception {
2011
2012        for (int i = 0; i < argv.length; i++) {
2013            final String expression = argv[i];
2014            System.out.println("# XPath expression: \""+expression+'"');
2015            try {
2016                SymbolTable symbolTable = new SymbolTable();
2017                XPath xpath = new XPath(expression, symbolTable, null);
2018                System.out.println("expanded xpath: \""+xpath.toString()+'"');
2019            }
2020            catch (XPathException e) {
2021                System.out.println("error: "+e.getMessage());
2022            }
2023        }
2024
2025    } // main(String[])
2026
2027} // class XPath
2028