2
* The Apache Software License, Version 1.1
5
* Copyright (c) 2000-2002 The Apache Software Foundation.
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
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
48
* ====================================================================
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/>.
58
package org.apache.xerces.impl.xpath;
60
import java.util.Vector;
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;
69
* Bare minimum XPath parser.
71
* @author Andy Clark, IBM
72
* @version $Id: XPath.java,v 1.13 2004/02/09 22:50:01 kohsuke Exp $
80
private static final boolean DEBUG_ALL = false;
82
private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false;
84
private static final boolean DEBUG_ANY = DEBUG_XPATH_PARSE;
91
protected String fExpression;
94
protected SymbolTable fSymbolTable;
96
/** Location paths. */
97
protected LocationPath[] fLocationPaths;
103
/** Constructs an XPath object from the specified expression. */
104
public XPath(String xpath, SymbolTable symbolTable,
105
NamespaceContext context)
106
throws XPathException {
108
fSymbolTable = symbolTable;
109
parseExpression(context);
110
} // <init>(String,SymbolTable,NamespaceContext)
117
* Returns a representation of all location paths for this XPath.
118
* XPath = locationPath ( '|' locationPath)
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();
126
} // getLocationPath(LocationPath)
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)
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++){
144
buf.append(fLocationPaths[i].toString());
146
return buf.toString();
147
} // toString():String
154
* Used by the {@link #parseExpression(NamespaceContext)} method
155
* to verify the assumption.
157
* If <tt>b</tt> is false, this method throws XPathException
158
* to report the error.
160
private static void check( boolean b ) throws XPathException {
161
if(!b) throw new XPathException("c-general-xpath");
165
* Used by the {@link #parseExpression(NamespaceContext)} method
166
* to build a {@link LocationPath} object from the accumulated
169
private LocationPath buildLocationPath( Vector stepsVector ) throws XPathException {
170
int size = stepsVector.size();
172
Step[] steps = new Step[size];
173
stepsVector.copyInto(steps);
174
stepsVector.removeAllElements();
176
return new LocationPath(steps);
180
* This method is implemented by using the XPathExprScanner and
181
* examining the list of tokens that it returns.
183
private void parseExpression(final NamespaceContext context)
184
throws XPathException {
187
final XPath.Tokens xtokens = new XPath.Tokens(fSymbolTable);
190
XPath.Scanner scanner = new XPath.Scanner(fSymbolTable) {
191
protected void addToken(XPath.Tokens tokens, int token)
192
throws XPathException {
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
204
super.addToken(tokens, token);
207
throw new XPathException("c-general-xpath");
211
int length = fExpression.length();
213
boolean success = scanner.scanExpr(fSymbolTable,
214
xtokens, fExpression, 0, length);
216
throw new XPathException("c-general-xpath");
218
//fTokens.dumpTokens();
219
Vector stepsVector = new Vector();
220
Vector locationPathsVector= new Vector();
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 '/'.
226
// this is to make sure we can detect a token list like
227
// 'abc' '/' '/' 'def' 'ghi'
228
boolean expectingStep = true;
230
while(xtokens.hasMore()) {
231
final int token = xtokens.nextToken();
234
case XPath.Tokens.EXPRTOKEN_OPERATOR_UNION :{
235
check(!expectingStep);
236
locationPathsVector.addElement(buildLocationPath(stepsVector));
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);
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);
262
case XPath.Tokens.EXPRTOKEN_PERIOD: {
263
check(expectingStep);
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) {
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);
278
if( xtokens.hasMore()
279
&& xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH){
284
axis = new Axis(Axis.DESCENDANT);
285
nodeTest = new NodeTest(NodeTest.NODE);
286
step = new Step(axis, nodeTest);
287
stepsVector.addElement(step);
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");
300
case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: {
301
check(!expectingStep);
306
// we should have covered all the tokens that we can possibly see.
307
throw new InternalError();
311
check(!expectingStep);
313
locationPathsVector.addElement(buildLocationPath(stepsVector));
315
// save location path
316
fLocationPaths=new LocationPath[locationPathsVector.size()];
317
locationPathsVector.copyInto(fLocationPaths);
320
if (DEBUG_XPATH_PARSE) {
321
System.out.println(">>> "+fLocationPaths);
324
} // parseExpression(SymbolTable,NamespaceContext)
327
* Used by {@link #parseExpression} to parse a node test
328
* from the token list.
330
private NodeTest parseNodeTest( int typeToken, Tokens xtokens, NamespaceContext context )
331
throws XPathException {
333
case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
334
return new NodeTest(NodeTest.WILDCARD);
336
case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
337
case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME:
338
// consume QName token
339
String prefix = xtokens.nextTokenAsString();
341
if (context != null && prefix != XMLSymbols.EMPTY_STRING) {
342
uri = context.getURI(prefix);
344
if (prefix != XMLSymbols.EMPTY_STRING && context != null && uri == null) {
345
throw new XPathException("c-general-xpath-ns");
348
if (typeToken==XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE)
349
return new NodeTest(prefix,uri);
351
String localpart = xtokens.nextTokenAsString();
352
String rawname = prefix != XMLSymbols.EMPTY_STRING
353
? fSymbolTable.addSymbol(prefix+':'+localpart)
356
return new NodeTest(new QName(prefix, localpart, rawname, uri));
360
throw new InternalError();
369
// location path information
372
* A location path representation for an XPath expression.
374
* @author Andy Clark, IBM
376
public static class LocationPath
377
implements Cloneable {
383
/** List of steps. */
390
/** Creates a location path from a series of steps. */
391
public LocationPath(Step[] steps) {
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();
401
} // <init>(LocationPath)
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) ){
415
str.append(steps[i].toString());
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
422
String s = super.toString();
423
str.append(s.substring(s.indexOf('@')));
426
return str.toString();
427
} // toString():String
429
/** Returns a clone of this object. */
430
public Object clone() {
431
return new LocationPath(this);
434
} // class locationPath
437
* A location path step comprised of an axis and node test.
439
* @author Andy Clark, IBM
441
public static class Step
442
implements Cloneable {
452
public NodeTest nodeTest;
458
/** Constructs a step from an axis and node test. */
459
public Step(Axis axis, NodeTest nodeTest) {
461
this.nodeTest = nodeTest;
462
} // <init>(Axis,NodeTest)
464
/** Copy constructor. */
465
protected Step(Step step) {
466
axis = (Axis)step.axis.clone();
467
nodeTest = (NodeTest)step.nodeTest.clone();
474
/** Returns a string representation of this object. */
475
public String toString() {
476
if (axis.type == Axis.SELF) {
479
if (axis.type == Axis.ATTRIBUTE) {
480
return "@" + nodeTest.toString();
482
if (axis.type == Axis.CHILD) {
483
return nodeTest.toString();
485
if (axis.type == Axis.DESCENDANT) {
488
return "??? ("+axis.type+')';
489
} // toString():String
491
/** Returns a clone of this object. */
492
public Object clone() {
493
return new Step(this);
501
* @author Andy Clark, IBM
503
public static class Axis
504
implements Cloneable {
511
public static final short CHILD = 1;
513
/** Type: attribute. */
514
public static final short ATTRIBUTE = 2;
517
public static final short SELF = 3;
520
/** Type: descendant. */
521
public static final short DESCENDANT = 4;
533
/** Constructs an axis with the specified type. */
534
public Axis(short type) {
538
/** Copy constructor. */
539
protected Axis(Axis axis) {
547
/** Returns a string representation of this object. */
548
public String toString() {
550
case CHILD: return "child";
551
case ATTRIBUTE: return "attribute";
552
case SELF: return "self";
553
case DESCENDANT: return "descendant";
556
} // toString():String
558
/** Returns a clone of this object. */
559
public Object clone() {
560
return new Axis(this);
568
* @author Andy Clark, IBM
570
public static class NodeTest
571
implements Cloneable {
577
/** Type: qualified name. */
578
public static final short QNAME = 1;
580
/** Type: wildcard. */
581
public static final short WILDCARD = 2;
584
public static final short NODE = 3;
586
/** Type: namespace */
587
public static final short NAMESPACE= 4;
593
/** Node test type. */
596
/** Node qualified name. */
597
public final QName name = new QName();
603
/** Constructs a node test of type WILDCARD or NODE. */
604
public NodeTest(short type) {
608
/** Constructs a node test of type QName. */
609
public NodeTest(QName name) {
611
this.name.setValues(name);
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)
619
/** Copy constructor. */
620
public NodeTest(NodeTest nodeTest) {
621
type = nodeTest.type;
622
name.setValues(nodeTest.name);
623
} // <init>(NodeTest)
629
/** Returns a string representation of this object. */
630
public String toString() {
634
if (name.prefix.length() !=0) {
635
if (name.uri != null) {
636
return name.prefix+':'+name.localpart;
638
return "{"+name.uri+'}'+name.prefix+':'+name.localpart;
640
return name.localpart;
643
if (name.prefix.length() !=0) {
644
if (name.uri != null) {
645
return name.prefix+":*";
647
return "{"+name.uri+'}'+name.prefix+":*";
660
} // toString():String
662
/** Returns a clone of this object. */
663
public Object clone() {
664
return new NodeTest(this);
669
// xpath implementation
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
680
* @author Glenn Marcy, IBM
681
* @author Andy Clark, IBM
683
* @version $Id: XPath.java,v 1.13 2004/02/09 22:50:01 kohsuke Exp $
685
private static final class Tokens {
687
static final boolean DUMP_TOKENS = false;
690
* [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
691
* | NameTest | NodeType | Operator | FunctionName
692
* | AxisName | Literal | Number | VariableReference
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,
703
EXPRTOKEN_DOUBLE_COLON = 8,
705
// [37] NameTest ::= '*' | NCName ':' '*' | QName
707
// followed by symbol handle of NCName or QName
709
EXPRTOKEN_NAMETEST_ANY = 9,
710
EXPRTOKEN_NAMETEST_NAMESPACE = 10,
711
EXPRTOKEN_NAMETEST_QNAME = 11,
713
// [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
715
EXPRTOKEN_NODETYPE_COMMENT = 12,
716
EXPRTOKEN_NODETYPE_TEXT = 13,
717
EXPRTOKEN_NODETYPE_PI = 14,
718
EXPRTOKEN_NODETYPE_NODE = 15,
720
// [32] Operator ::= OperatorName
721
// | MultiplyOperator
722
// | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
723
// [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
724
// [34] MultiplyOperator ::= '*'
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,
743
//EXPRTOKEN_FIRST_OPERATOR = EXPRTOKEN_OPERATOR_AND,
744
//EXPRTOKEN_LAST_OPERATOR = EXPRTOKEN_OPERATOR_GREATER_EQUAL,
747
// [35] FunctionName ::= QName - NodeType
749
// followed by symbol handle
751
EXPRTOKEN_FUNCTION_NAME = 32,
753
// [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
756
// | 'descendant' | 'descendant-or-self'
757
// | 'following' | 'following-sibling'
760
// | 'preceding' | 'preceding-sibling'
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,
777
// [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
779
// followed by symbol handle for literal
781
EXPRTOKEN_LITERAL = 46,
783
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
784
// [31] Digits ::= [0-9]+
786
// followed by number handle
788
EXPRTOKEN_NUMBER = 47,
790
// [36] VariableReference ::= '$' QName
792
// followed by symbol handle for QName
794
EXPRTOKEN_VARIABLE_REFERENCE = 48;
796
private static final String[] fgTokenNames = {
797
"EXPRTOKEN_OPEN_PAREN",
798
"EXPRTOKEN_CLOSE_PAREN",
799
"EXPRTOKEN_OPEN_BRACKET",
800
"EXPRTOKEN_CLOSE_BRACKET",
802
"EXPRTOKEN_DOUBLE_PERIOD",
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",
845
"EXPRTOKEN_VARIABLE_REFERENCE"
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
855
private SymbolTable fSymbolTable;
857
// REVISIT: Code something better here. -Ac
858
private java.util.Hashtable fSymbolMapping = new java.util.Hashtable();
860
// REVISIT: Code something better here. -Ac
861
private java.util.Hashtable fTokenNames = new java.util.Hashtable();
864
* Current position in the token list.
866
private int fCurrentTokenIndex;
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",
881
for (int i = 0; i < symbols.length; i++) {
882
fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]), new Integer(i));
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");
939
// public String getTokenName(int token) {
940
// if (token < 0 || token >= fgTokenNames.length)
942
// return fgTokenNames[token];
945
public String getTokenString(int token) {
946
return (String)fTokenNames.get(new Integer(token));
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);
955
addToken(tokenInt.intValue());
958
public void addToken(int token) {
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;
969
// public int getTokenCount() {
970
// return fTokenCount;
972
// public int getToken(int tokenIndex) {
973
// return fTokens[tokenIndex];
977
* Resets the current position to the head of the token list.
979
public void rewind() {
980
fCurrentTokenIndex=0;
983
* Returns true if the {@link #getNextToken()} method
984
* returns a valid token.
986
public boolean hasMore() {
987
return fCurrentTokenIndex<fTokenCount;
990
* Obtains the token at the current position, then advance
991
* the current position by one.
993
* If there's no such next token, this method throws
994
* <tt>new XPathException("c-general-xpath");</tt>.
996
public int nextToken() throws XPathException {
997
if( fCurrentTokenIndex==fTokenCount )
998
throw new XPathException("c-xpath-general");
999
return fTokens[fCurrentTokenIndex++];
1002
* Obtains the token at the current position, without advancing
1003
* the current position.
1005
* If there's no such next token, this method throws
1006
* <tt>new XPathException("c-general-xpath");</tt>.
1008
public int peekToken() throws XPathException {
1009
if( fCurrentTokenIndex==fTokenCount )
1010
throw new XPathException("c-xpath-general");
1011
return fTokens[fCurrentTokenIndex];
1014
* Obtains the token at the current position as a String.
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>.
1020
public String nextTokenAsString() throws XPathException {
1021
String s = getTokenString(nextToken());
1022
if(s==null) throw new XPathException("c-xpath-general");
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/>");
1033
case EXPRTOKEN_CLOSE_PAREN:
1034
System.out.print("<CLOSE_PAREN/>");
1036
case EXPRTOKEN_OPEN_BRACKET:
1037
System.out.print("<OPEN_BRACKET/>");
1039
case EXPRTOKEN_CLOSE_BRACKET:
1040
System.out.print("<CLOSE_BRACKET/>");
1042
case EXPRTOKEN_PERIOD:
1043
System.out.print("<PERIOD/>");
1045
case EXPRTOKEN_DOUBLE_PERIOD:
1046
System.out.print("<DOUBLE_PERIOD/>");
1048
case EXPRTOKEN_ATSIGN:
1049
System.out.print("<ATSIGN/>");
1051
case EXPRTOKEN_COMMA:
1052
System.out.print("<COMMA/>");
1054
case EXPRTOKEN_DOUBLE_COLON:
1055
System.out.print("<DOUBLE_COLON/>");
1057
case EXPRTOKEN_NAMETEST_ANY:
1058
System.out.print("<NAMETEST_ANY/>");
1060
case EXPRTOKEN_NAMETEST_NAMESPACE:
1061
System.out.print("<NAMETEST_NAMESPACE");
1062
System.out.print(" prefix=\"" + getTokenString(fTokens[++i]) + "\"");
1063
System.out.print("/>");
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("/>");
1072
case EXPRTOKEN_NODETYPE_COMMENT:
1073
System.out.print("<NODETYPE_COMMENT/>");
1075
case EXPRTOKEN_NODETYPE_TEXT:
1076
System.out.print("<NODETYPE_TEXT/>");
1078
case EXPRTOKEN_NODETYPE_PI:
1079
System.out.print("<NODETYPE_PI/>");
1081
case EXPRTOKEN_NODETYPE_NODE:
1082
System.out.print("<NODETYPE_NODE/>");
1084
case EXPRTOKEN_OPERATOR_AND:
1085
System.out.print("<OPERATOR_AND/>");
1087
case EXPRTOKEN_OPERATOR_OR:
1088
System.out.print("<OPERATOR_OR/>");
1090
case EXPRTOKEN_OPERATOR_MOD:
1091
System.out.print("<OPERATOR_MOD/>");
1093
case EXPRTOKEN_OPERATOR_DIV:
1094
System.out.print("<OPERATOR_DIV/>");
1096
case EXPRTOKEN_OPERATOR_MULT:
1097
System.out.print("<OPERATOR_MULT/>");
1099
case EXPRTOKEN_OPERATOR_SLASH:
1100
System.out.print("<OPERATOR_SLASH/>");
1101
if (i + 1 < fTokenCount) {
1102
System.out.println();
1103
System.out.print(" ");
1106
case EXPRTOKEN_OPERATOR_DOUBLE_SLASH:
1107
System.out.print("<OPERATOR_DOUBLE_SLASH/>");
1109
case EXPRTOKEN_OPERATOR_UNION:
1110
System.out.print("<OPERATOR_UNION/>");
1112
case EXPRTOKEN_OPERATOR_PLUS:
1113
System.out.print("<OPERATOR_PLUS/>");
1115
case EXPRTOKEN_OPERATOR_MINUS:
1116
System.out.print("<OPERATOR_MINUS/>");
1118
case EXPRTOKEN_OPERATOR_EQUAL:
1119
System.out.print("<OPERATOR_EQUAL/>");
1121
case EXPRTOKEN_OPERATOR_NOT_EQUAL:
1122
System.out.print("<OPERATOR_NOT_EQUAL/>");
1124
case EXPRTOKEN_OPERATOR_LESS:
1125
System.out.print("<OPERATOR_LESS/>");
1127
case EXPRTOKEN_OPERATOR_LESS_EQUAL:
1128
System.out.print("<OPERATOR_LESS_EQUAL/>");
1130
case EXPRTOKEN_OPERATOR_GREATER:
1131
System.out.print("<OPERATOR_GREATER/>");
1133
case EXPRTOKEN_OPERATOR_GREATER_EQUAL:
1134
System.out.print("<OPERATOR_GREATER_EQUAL/>");
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("/>");
1143
case EXPRTOKEN_AXISNAME_ANCESTOR:
1144
System.out.print("<AXISNAME_ANCESTOR/>");
1146
case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
1147
System.out.print("<AXISNAME_ANCESTOR_OR_SELF/>");
1149
case EXPRTOKEN_AXISNAME_ATTRIBUTE:
1150
System.out.print("<AXISNAME_ATTRIBUTE/>");
1152
case EXPRTOKEN_AXISNAME_CHILD:
1153
System.out.print("<AXISNAME_CHILD/>");
1155
case EXPRTOKEN_AXISNAME_DESCENDANT:
1156
System.out.print("<AXISNAME_DESCENDANT/>");
1158
case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
1159
System.out.print("<AXISNAME_DESCENDANT_OR_SELF/>");
1161
case EXPRTOKEN_AXISNAME_FOLLOWING:
1162
System.out.print("<AXISNAME_FOLLOWING/>");
1164
case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
1165
System.out.print("<AXISNAME_FOLLOWING_SIBLING/>");
1167
case EXPRTOKEN_AXISNAME_NAMESPACE:
1168
System.out.print("<AXISNAME_NAMESPACE/>");
1170
case EXPRTOKEN_AXISNAME_PARENT:
1171
System.out.print("<AXISNAME_PARENT/>");
1173
case EXPRTOKEN_AXISNAME_PRECEDING:
1174
System.out.print("<AXISNAME_PRECEDING/>");
1176
case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
1177
System.out.print("<AXISNAME_PRECEDING_SIBLING/>");
1179
case EXPRTOKEN_AXISNAME_SELF:
1180
System.out.print("<AXISNAME_SELF/>");
1182
case EXPRTOKEN_LITERAL:
1183
System.out.print("<LITERAL");
1184
System.out.print(" value=\"" + getTokenString(fTokens[++i]) + "\"");
1185
System.out.print("/>");
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("/>");
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("/>");
1201
System.out.println("<???/>");
1204
System.out.println();
1211
* @author Glenn Marcy, IBM
1212
* @author Andy Clark, IBM
1214
* @version $Id: XPath.java,v 1.13 2004/02/09 22:50:01 kohsuke Exp $
1216
private static class Scanner {
1219
* 7-bit ASCII subset
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
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)
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
1278
/** Symbol table. */
1279
private SymbolTable fSymbolTable;
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();
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();
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();
1311
/** Constructs an XPath expression scanner. */
1312
public Scanner(SymbolTable symbolTable) {
1314
// save pool and tokens
1315
fSymbolTable = symbolTable;
1317
} // <init>(SymbolTable)
1322
public boolean scanExpr(SymbolTable symbolTable,
1323
XPath.Tokens tokens, String data,
1324
int currentOffset, int endOffset)
1325
throws XPathException {
1328
String nameHandle, prefixHandle;
1329
boolean starIsMultiplyOperator = false;
1333
if (currentOffset == endOffset) {
1336
ch = data.charAt(currentOffset);
1338
// [39] ExprWhitespace ::= S
1340
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1341
if (++currentOffset == endOffset) {
1344
ch = data.charAt(currentOffset);
1346
if (currentOffset == endOffset) {
1350
// [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
1351
// | NameTest | NodeType | Operator | FunctionName
1352
// | AxisName | Literal | Number | VariableReference
1354
byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch];
1356
case CHARTYPE_OPEN_PAREN: // '('
1357
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1358
starIsMultiplyOperator = false;
1359
if (++currentOffset == endOffset) {
1363
case CHARTYPE_CLOSE_PAREN: // ')'
1364
addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN);
1365
starIsMultiplyOperator = true;
1366
if (++currentOffset == endOffset) {
1370
case CHARTYPE_OPEN_BRACKET: // '['
1371
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_BRACKET);
1372
starIsMultiplyOperator = false;
1373
if (++currentOffset == endOffset) {
1377
case CHARTYPE_CLOSE_BRACKET: // ']'
1378
addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET);
1379
starIsMultiplyOperator = true;
1380
if (++currentOffset == endOffset) {
1385
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1388
case CHARTYPE_PERIOD: // '.', '..' or '.' Digits
1389
if (currentOffset + 1 == endOffset) {
1390
addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1391
starIsMultiplyOperator = true;
1395
ch = data.charAt(currentOffset + 1);
1396
if (ch == '.') { // '..'
1397
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD);
1398
starIsMultiplyOperator = true;
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;
1408
} else if (ch == '|') {
1409
addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1410
starIsMultiplyOperator = true;
1413
} else if (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1414
// this is legal if the next token is non-existent or |
1416
if (++currentOffset == endOffset) {
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;
1426
throw new XPathException ("c-general-xpath");
1428
throw new XPathException ("c-general-xpath");
1430
if (currentOffset == endOffset) {
1434
case CHARTYPE_ATSIGN: // '@'
1435
addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN);
1436
starIsMultiplyOperator = false;
1437
if (++currentOffset == endOffset) {
1441
case CHARTYPE_COMMA: // ','
1442
addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA);
1443
starIsMultiplyOperator = false;
1444
if (++currentOffset == endOffset) {
1448
case CHARTYPE_COLON: // '::'
1449
if (++currentOffset == endOffset) {
1450
// System.out.println("abort 1a");
1451
return false; // REVISIT
1453
ch = data.charAt(currentOffset);
1455
// System.out.println("abort 1b");
1456
return false; // REVISIT
1458
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1459
starIsMultiplyOperator = false;
1460
if (++currentOffset == endOffset) {
1464
case CHARTYPE_SLASH: // '/' and '//'
1465
if (++currentOffset == endOffset) {
1466
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1467
starIsMultiplyOperator = false;
1470
ch = data.charAt(currentOffset);
1471
if (ch == '/') { // '//'
1472
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH);
1473
starIsMultiplyOperator = false;
1474
if (++currentOffset == endOffset) {
1478
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1479
starIsMultiplyOperator = false;
1482
case CHARTYPE_UNION: // '|'
1483
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_UNION);
1484
starIsMultiplyOperator = false;
1485
if (++currentOffset == endOffset) {
1489
case CHARTYPE_PLUS: // '+'
1490
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS);
1491
starIsMultiplyOperator = false;
1492
if (++currentOffset == endOffset) {
1496
case CHARTYPE_MINUS: // '-'
1497
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS);
1498
starIsMultiplyOperator = false;
1499
if (++currentOffset == endOffset) {
1503
case CHARTYPE_EQUAL: // '='
1504
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL);
1505
starIsMultiplyOperator = false;
1506
if (++currentOffset == endOffset) {
1510
case CHARTYPE_EXCLAMATION: // '!='
1511
if (++currentOffset == endOffset) {
1512
// System.out.println("abort 2a");
1513
return false; // REVISIT
1515
ch = data.charAt(currentOffset);
1517
// System.out.println("abort 2b");
1518
return false; // REVISIT
1520
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL);
1521
starIsMultiplyOperator = false;
1522
if (++currentOffset == endOffset) {
1526
case CHARTYPE_LESS: // '<' and '<='
1527
if (++currentOffset == endOffset) {
1528
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1529
starIsMultiplyOperator = false;
1532
ch = data.charAt(currentOffset);
1533
if (ch == '=') { // '<='
1534
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL);
1535
starIsMultiplyOperator = false;
1536
if (++currentOffset == endOffset) {
1540
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1541
starIsMultiplyOperator = false;
1544
case CHARTYPE_GREATER: // '>' and '>='
1545
if (++currentOffset == endOffset) {
1546
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1547
starIsMultiplyOperator = false;
1550
ch = data.charAt(currentOffset);
1551
if (ch == '=') { // '>='
1552
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL);
1553
starIsMultiplyOperator = false;
1554
if (++currentOffset == endOffset) {
1558
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1559
starIsMultiplyOperator = false;
1563
// [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
1565
case CHARTYPE_QUOTE: // '\"' or '\''
1567
if (++currentOffset == endOffset) {
1568
// System.out.println("abort 2c");
1569
return false; // REVISIT
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
1578
ch = data.charAt(currentOffset);
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) {
1589
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1590
// [31] Digits ::= [0-9]+
1592
case CHARTYPE_DIGIT:
1593
addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1594
starIsMultiplyOperator = true;
1595
currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
1598
// [36] VariableReference ::= '$' QName
1600
case CHARTYPE_DOLLAR:
1601
if (++currentOffset == endOffset) {
1602
// System.out.println("abort 3a");
1603
return false; // REVISIT
1605
nameOffset = currentOffset;
1606
currentOffset = scanNCName(data, endOffset, currentOffset);
1607
if (currentOffset == nameOffset) {
1608
// System.out.println("abort 3b");
1609
return false; // REVISIT
1611
if (currentOffset < endOffset) {
1612
ch = data.charAt(currentOffset);
1617
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1619
prefixHandle = XMLSymbols.EMPTY_STRING;
1621
prefixHandle = nameHandle;
1622
if (++currentOffset == endOffset) {
1623
// System.out.println("abort 4a");
1624
return false; // REVISIT
1626
nameOffset = currentOffset;
1627
currentOffset = scanNCName(data, endOffset, currentOffset);
1628
if (currentOffset == nameOffset) {
1629
// System.out.println("abort 4b");
1630
return false; // REVISIT
1632
if (currentOffset < endOffset) {
1633
ch = data.charAt(currentOffset);
1638
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1640
addToken(tokens, XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE);
1641
starIsMultiplyOperator = true;
1642
tokens.addToken(prefixHandle);
1643
tokens.addToken(nameHandle);
1646
// [37] NameTest ::= '*' | NCName ':' '*' | QName
1647
// [34] MultiplyOperator ::= '*'
1649
case CHARTYPE_STAR: // '*'
1651
// 3.7 Lexical Structure
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.
1656
// Otherwise, the token must not be recognized as a MultiplyOperator.
1658
if (starIsMultiplyOperator) {
1659
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MULT);
1660
starIsMultiplyOperator = false;
1662
addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_ANY);
1663
starIsMultiplyOperator = true;
1665
if (++currentOffset == endOffset) {
1670
// NCName, QName and non-terminals
1672
case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic)
1673
case CHARTYPE_LETTER:
1674
case CHARTYPE_UNDERSCORE:
1676
// 3.7 Lexical Structure
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.
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.
1684
// If the two characters following an NCName (possibly after intervening ExprWhitespace)
1685
// are ::, then the token must be recognized as an AxisName.
1687
// Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1688
// FunctionName, or an AxisName.
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)
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')
1702
nameOffset = currentOffset;
1703
currentOffset = scanNCName(data, endOffset, currentOffset);
1704
if (currentOffset == nameOffset) {
1705
// System.out.println("abort 4c");
1706
return false; // REVISIT
1708
if (currentOffset < endOffset) {
1709
ch = data.charAt(currentOffset);
1714
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1715
boolean isNameTestNCName = false;
1716
boolean isAxisName = false;
1717
prefixHandle = XMLSymbols.EMPTY_STRING;
1719
if (++currentOffset == endOffset) {
1720
// System.out.println("abort 5");
1721
return false; // REVISIT
1723
ch = data.charAt(currentOffset);
1725
if (++currentOffset < endOffset) {
1726
ch = data.charAt(currentOffset);
1728
isNameTestNCName = true;
1729
} else if (ch == ':') {
1730
if (++currentOffset < endOffset) {
1731
ch = data.charAt(currentOffset);
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
1742
if (currentOffset < endOffset) {
1743
ch = data.charAt(currentOffset);
1748
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
1752
// [39] ExprWhitespace ::= S
1754
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
1755
if (++currentOffset == endOffset) {
1758
ch = data.charAt(currentOffset);
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.
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;
1778
// System.out.println("abort 6");
1779
return false; // REVISIT
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
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.
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);
1804
addToken(tokens, XPath.Tokens.EXPRTOKEN_FUNCTION_NAME);
1805
tokens.addToken(prefixHandle);
1806
tokens.addToken(nameHandle);
1808
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1809
starIsMultiplyOperator = false;
1810
if (++currentOffset == endOffset) {
1816
// If the two characters following an NCName (possibly after intervening ExprWhitespace)
1817
// are ::, then the token must be recognized as an AxisName.
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);
1849
// System.out.println("abort 9");
1850
return false; // REVISIT
1852
if (isNameTestNCName) {
1853
// System.out.println("abort 10");
1854
return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required
1856
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1857
starIsMultiplyOperator = false;
1860
if (++currentOffset == endOffset) {
1867
// Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1868
// FunctionName, or an AxisName.
1870
if (isNameTestNCName) {
1871
addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE);
1872
starIsMultiplyOperator = true;
1873
tokens.addToken(nameHandle);
1875
addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME);
1876
starIsMultiplyOperator = true;
1877
tokens.addToken(prefixHandle);
1878
tokens.addToken(nameHandle);
1883
if (XPath.Tokens.DUMP_TOKENS) {
1884
tokens.dumpTokens();
1889
// [5] NCName ::= (Letter | '_') (NCNameChar)*
1890
// [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
1892
int scanNCName(String data, int endOffset, int currentOffset) {
1893
int ch = data.charAt(currentOffset);
1895
if (!XMLChar.isNameStart(ch))
1896
/*** // REVISIT: Make sure this is a negation. ***
1897
if ((XMLCharacterProperties.fgCharFlags[ch] &
1898
XMLCharacterProperties.E_InitialNameCharFlag) == 0)
1901
return currentOffset;
1905
byte chartype = fASCIICharMap[ch];
1906
if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
1907
return currentOffset;
1910
while (++currentOffset < endOffset) {
1911
ch = data.charAt(currentOffset);
1913
if (!XMLChar.isName(ch))
1914
/*** // REVISIT: Make sure this is a negation. ***
1915
if ((XMLCharacterProperties.fgCharFlags[ch] &
1916
XMLCharacterProperties.E_NameCharFlag) == 0)
1923
byte chartype = fASCIICharMap[ch];
1924
if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
1925
chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
1926
chartype != CHARTYPE_UNDERSCORE)
1932
return currentOffset;
1935
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1936
// [31] Digits ::= [0-9]+
1938
private int scanNumber(XPath.Tokens tokens, String/*byte[]*/ data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) {
1939
int ch = data.charAt(currentOffset);
1942
while (ch >= '0' && ch <= '9') {
1943
whole = (whole * 10) + (ch - '0');
1944
if (++currentOffset == endOffset) {
1947
ch = data.charAt(currentOffset);
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) {
1958
ch = data.charAt(currentOffset);
1962
part = tokens.addSymbol(data, start, currentOffset - start, encoding);
1964
throw new RuntimeException("find a solution!");
1965
//part = fStringPool.addSymbol(data.substring(start, currentOffset));
1970
tokens.addToken(whole);
1971
tokens.addToken(part);
1972
return currentOffset;
1976
// Protected methods
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.
1987
protected void addToken(XPath.Tokens tokens, int token)
1988
throws XPathException {
1989
tokens.addToken(token);
1998
/** Main program entry. */
1999
public static void main(String[] argv) throws Exception {
2001
for (int i = 0; i < argv.length; i++) {
2002
final String expression = argv[i];
2003
System.out.println("# XPath expression: \""+expression+'"');
2005
SymbolTable symbolTable = new SymbolTable();
2006
XPath xpath = new XPath(expression, symbolTable, null);
2007
System.out.println("expanded xpath: \""+xpath.toString()+'"');
2009
catch (XPathException e) {
2010
System.out.println("error: "+e.getMessage());