~ubuntu-branches/ubuntu/karmic/libxerces2-java/karmic

« back to all changes in this revision

Viewing changes to src/org/apache/xerces/impl/xpath/XPath.java

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Gybas
  • Date: 2004-06-06 18:00:26 UTC
  • Revision ID: james.westby@ubuntu.com-20040606180026-a3vh56uc95hjbyfh
Tags: upstream-2.6.2
ImportĀ upstreamĀ versionĀ 2.6.2

Show diffs side-by-side

added added

removed removed

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