org.apache.xerces.impl.xpath.XPath Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xercesImpl Show documentation
Show all versions of xercesImpl Show documentation
Xerces2 is the next generation of high performance, fully compliant XML parsers in the Apache Xerces family.
This new version of Xerces introduces the Xerces Native Interface (XNI), a complete framework for building
parser components and configurations that is extremely modular and easy to program. The Apache Xerces2 parser is
the reference implementation of XNI but other parser components, configurations, and parsers can be written
using the Xerces Native Interface. For complete design and implementation documents, refer to the XNI Manual.
Xerces2 is a fully conforming XML Schema 1.0 processor. A partial experimental implementation of the XML Schema
1.1 Structures and Datatypes Working Drafts (December 2009) and an experimental implementation of the XML Schema
Definition Language (XSD): Component Designators (SCD) Candidate Recommendation (January 2010) are provided for
evaluation. For more information, refer to the XML Schema page. Xerces2 also provides a complete implementation
of the Document Object Model Level 3 Core and Load/Save W3C Recommendations and provides a complete
implementation of the XML Inclusions (XInclude) W3C Recommendation. It also provides support for OASIS XML
Catalogs v1.1. Xerces2 is able to parse documents written according to the XML 1.1 Recommendation, except that
it does not yet provide an option to enable normalization checking as described in section 2.13 of this
specification. It also handles namespaces according to the XML Namespaces 1.1 Recommendation, and will correctly
serialize XML 1.1 documents if the DOM level 3 load/save APIs are in use.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xerces.impl.xpath;
import java.util.ArrayList;
import java.util.Vector;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
/**
* Bare minimum XPath parser.
*
* @xerces.internal
*
* @author Andy Clark, IBM
* @version $Id: XPath.java 965250 2010-07-18 16:04:58Z mrglavas $
*/
public class XPath {
//
// Constants
//
private static final boolean DEBUG_ALL = false;
private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false;
private static final boolean DEBUG_ANY = DEBUG_XPATH_PARSE;
//
// Data
//
/** Expression. */
protected final String fExpression;
/** Symbol table. */
protected final SymbolTable fSymbolTable;
/** Location paths. */
protected final LocationPath[] fLocationPaths;
//
// Constructors
//
/** Constructs an XPath object from the specified expression. */
public XPath(String xpath, SymbolTable symbolTable,
NamespaceContext context)
throws XPathException {
fExpression = xpath;
fSymbolTable = symbolTable;
fLocationPaths = parseExpression(context);
if (DEBUG_XPATH_PARSE) {
System.out.println(">>> "+fLocationPaths);
}
} // (String,SymbolTable,NamespaceContext)
//
// Public methods
//
/**
* Returns a representation of all location paths for this XPath.
* XPath = locationPath ( '|' locationPath)
*/
public LocationPath[] getLocationPaths() {
LocationPath[] ret=new LocationPath[fLocationPaths.length];
for (int i=0;i0){
buf.append('|');
}
buf.append(fLocationPaths[i].toString());
}
return buf.toString();
} // toString():String
//
// Private methods
//
/**
* Used by the {@link #parseExpression(NamespaceContext)} method
* to verify the assumption.
*
* If b is false, this method throws XPathException
* to report the error.
*/
private static void check( boolean b ) throws XPathException {
if(!b) throw new XPathException("c-general-xpath");
}
/**
* Used by the {@link #parseExpression(NamespaceContext)} method
* to build a {@link LocationPath} object from the accumulated
* {@link Step}s.
*/
private LocationPath buildLocationPath( Vector stepsVector ) throws XPathException {
int size = stepsVector.size();
check(size!=0);
Step[] steps = new Step[size];
stepsVector.copyInto(steps);
stepsVector.removeAllElements();
return new LocationPath(steps);
}
/**
* This method is implemented by using the XPathExprScanner and
* examining the list of tokens that it returns.
*/
private LocationPath[] parseExpression(final NamespaceContext context)
throws XPathException {
// tokens
final XPath.Tokens xtokens = new XPath.Tokens(fSymbolTable);
// scanner
XPath.Scanner scanner = new XPath.Scanner(fSymbolTable) {
protected void addToken(XPath.Tokens tokens, int token)
throws XPathException {
if (
token == XPath.Tokens.EXPRTOKEN_ATSIGN ||
token == XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME ||
token == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH ||
token == XPath.Tokens.EXPRTOKEN_PERIOD ||
token == XPath.Tokens.EXPRTOKEN_NAMETEST_ANY ||
token == XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE ||
token == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH ||
token == XPath.Tokens.EXPRTOKEN_OPERATOR_UNION ||
token == XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD ||
token == XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE ||
token == XPath.Tokens.EXPRTOKEN_DOUBLE_COLON
//
) {
super.addToken(tokens, token);
return;
}
throw new XPathException("c-general-xpath");
}
};
int length = fExpression.length();
boolean success = scanner.scanExpr(fSymbolTable,
xtokens, fExpression, 0, length);
if(!success)
throw new XPathException("c-general-xpath");
//fTokens.dumpTokens();
Vector stepsVector = new Vector();
ArrayList locationPathsVector= new ArrayList();
// true when the next token should be 'Step' (as defined in
// the production rule [3] of XML Schema P1 section 3.11.6
// if false, we are expecting either '|' or '/'.
//
// this is to make sure we can detect a token list like
// 'abc' '/' '/' 'def' 'ghi'
boolean expectingStep = true;
while (xtokens.hasMore()) {
final int token = xtokens.nextToken();
switch (token) {
case XPath.Tokens.EXPRTOKEN_OPERATOR_UNION :{
check(!expectingStep);
locationPathsVector.add(buildLocationPath(stepsVector));
expectingStep=true;
break;
}
case XPath.Tokens.EXPRTOKEN_ATSIGN: {
check(expectingStep);
Step step = new Step(
new Axis(Axis.ATTRIBUTE),
parseNodeTest(xtokens.nextToken(),xtokens,context));
stepsVector.addElement(step);
expectingStep=false;
break;
}
case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: {
check(expectingStep);
// If we got here we're expecting attribute::
if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
throw new XPathException("c-general-xpath");
}
Step step = new Step(
new Axis(Axis.ATTRIBUTE),
parseNodeTest(xtokens.nextToken(),xtokens,context));
stepsVector.addElement(step);
expectingStep = false;
break;
}
case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: {
check(expectingStep);
Step step = new Step(
new Axis(Axis.CHILD),
parseNodeTest(token,xtokens,context));
stepsVector.addElement(step);
expectingStep=false;
break;
}
case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD: {
check(expectingStep);
// If we got here we're expecting child::
if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
throw new XPathException("c-general-xpath");
}
Step step = new Step(
new Axis(Axis.CHILD),
parseNodeTest(xtokens.nextToken(),xtokens,context));
stepsVector.addElement(step);
expectingStep = false;
break;
}
case XPath.Tokens.EXPRTOKEN_PERIOD: {
check(expectingStep);
expectingStep=false;
// unless this is the first step in this location path,
// there's really no reason to keep them in LocationPath.
// This amounts to shorten "a/././b/./c" to "a/b/c".
// Also, the matcher fails to work correctly if XPath
// has those redundant dots.
if (stepsVector.size()==0) {
// build step
Axis axis = new Axis(Axis.SELF);
NodeTest nodeTest = new NodeTest(NodeTest.NODE);
Step step = new Step(axis, nodeTest);
stepsVector.addElement(step);
if( xtokens.hasMore()
&& xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH){
// consume '//'
xtokens.nextToken();
// build step
axis = new Axis(Axis.DESCENDANT);
nodeTest = new NodeTest(NodeTest.NODE);
step = new Step(axis, nodeTest);
stepsVector.addElement(step);
expectingStep=true;
}
}
break;
}
case XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH:{
// this cannot appear in an arbitrary position.
// it is only allowed right after '.' when
// '.' is the first token of a location path.
throw new XPathException("c-general-xpath");
}
case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON: {
// :: cannot appear in an arbitrary position.
// We only expect this token if the xpath
// contains child:: or attribute::
throw new XPathException("c-general-xpath");
}
case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: {
check(!expectingStep);
expectingStep=true;
break;
}
default:
// we should have covered all the tokens that we can possibly see.
throw new InternalError();
}
}
check(!expectingStep);
locationPathsVector.add(buildLocationPath(stepsVector));
// return location path
return (LocationPath[])locationPathsVector.toArray(new LocationPath[locationPathsVector.size()]);
} // parseExpression(SymbolTable,NamespaceContext)
/**
* Used by {@link #parseExpression} to parse a node test
* from the token list.
*/
private NodeTest parseNodeTest( int typeToken, Tokens xtokens, NamespaceContext context )
throws XPathException {
switch(typeToken) {
case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
return new NodeTest(NodeTest.WILDCARD);
case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME:
// consume QName token
String prefix = xtokens.nextTokenAsString();
String uri = null;
if (context != null && prefix != XMLSymbols.EMPTY_STRING) {
uri = context.getURI(prefix);
}
if (prefix != XMLSymbols.EMPTY_STRING && context != null && uri == null) {
throw new XPathException("c-general-xpath-ns");
}
if (typeToken==XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE)
return new NodeTest(prefix,uri);
String localpart = xtokens.nextTokenAsString();
String rawname = prefix != XMLSymbols.EMPTY_STRING
? fSymbolTable.addSymbol(prefix+':'+localpart)
: localpart;
return new NodeTest(new QName(prefix, localpart, rawname, uri));
default:
throw new XPathException("c-general-xpath");
}
}
//
// Classes
//
// location path information
/**
* A location path representation for an XPath expression.
*
* @xerces.internal
*
* @author Andy Clark, IBM
*/
public static class LocationPath
implements Cloneable {
//
// Data
//
/** List of steps. */
public final Step[] steps;
//
// Constructors
//
/** Creates a location path from a series of steps. */
public LocationPath(Step[] steps) {
this.steps = steps;
} // (Step[])
/** Copy constructor. */
protected LocationPath(LocationPath path) {
steps = new Step[path.steps.length];
for (int i = 0; i < steps.length; i++) {
steps[i] = (Step)path.steps[i].clone();
}
} // (LocationPath)
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
StringBuffer str = new StringBuffer();
for (int i = 0; i < steps.length; i++) {
if (i > 0 && (steps[i-1].axis.type!=Axis.DESCENDANT
&& steps[i].axis.type!=Axis.DESCENDANT) ){
str.append('/');
}
str.append(steps[i].toString());
}
// DEBUG: This code is just for debugging and should *not*
// be left in because it will mess up hashcodes of
// serialized versions of this object. -Ac
if (false) {
str.append('[');
String s = super.toString();
str.append(s.substring(s.indexOf('@')));
str.append(']');
}
return str.toString();
} // toString():String
/** Returns a clone of this object. */
public Object clone() {
return new LocationPath(this);
} // clone():Object
} // class locationPath
/**
* A location path step comprised of an axis and node test.
*
* @xerces.internal
*
* @author Andy Clark, IBM
*/
public static class Step
implements Cloneable {
//
// Data
//
/** Axis. */
public final Axis axis;
/** Node test. */
public final NodeTest nodeTest;
//
// Constructors
//
/** Constructs a step from an axis and node test. */
public Step(Axis axis, NodeTest nodeTest) {
this.axis = axis;
this.nodeTest = nodeTest;
} // (Axis,NodeTest)
/** Copy constructor. */
protected Step(Step step) {
axis = (Axis)step.axis.clone();
nodeTest = (NodeTest)step.nodeTest.clone();
} // (Step)
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
if (axis.type == Axis.SELF) {
return ".";
}
if (axis.type == Axis.ATTRIBUTE) {
return "@" + nodeTest.toString();
}
if (axis.type == Axis.CHILD) {
return nodeTest.toString();
}
if (axis.type == Axis.DESCENDANT) {
return "//";
}
return "??? ("+axis.type+')';
} // toString():String
/** Returns a clone of this object. */
public Object clone() {
return new Step(this);
} // clone():Object
} // class Step
/**
* Axis.
*
* @xerces.internal
*
* @author Andy Clark, IBM
*/
public static class Axis
implements Cloneable {
//
// Constants
//
/** Type: child. */
public static final short CHILD = 1;
/** Type: attribute. */
public static final short ATTRIBUTE = 2;
/** Type: self. */
public static final short SELF = 3;
/** Type: descendant. */
public static final short DESCENDANT = 4;
//
// Data
//
/** Axis type. */
public final short type;
//
// Constructors
//
/** Constructs an axis with the specified type. */
public Axis(short type) {
this.type = type;
} // (short)
/** Copy constructor. */
protected Axis(Axis axis) {
type = axis.type;
} // (Axis)
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
switch (type) {
case CHILD: return "child";
case ATTRIBUTE: return "attribute";
case SELF: return "self";
case DESCENDANT: return "descendant";
}
return "???";
} // toString():String
/** Returns a clone of this object. */
public Object clone() {
return new Axis(this);
} // clone():Object
} // class Axis
/**
* Node test.
*
* @xerces.internal
*
* @author Andy Clark, IBM
*/
public static class NodeTest
implements Cloneable {
//
// Constants
//
/** Type: qualified name. */
public static final short QNAME = 1;
/** Type: wildcard. */
public static final short WILDCARD = 2;
/** Type: node. */
public static final short NODE = 3;
/** Type: namespace */
public static final short NAMESPACE= 4;
//
// Data
//
/** Node test type. */
public final short type;
/** Node qualified name. */
public final QName name = new QName();
//
// Constructors
//
/** Constructs a node test of type WILDCARD or NODE. */
public NodeTest(short type) {
this.type = type;
} // (int)
/** Constructs a node test of type QName. */
public NodeTest(QName name) {
this.type = QNAME;
this.name.setValues(name);
} // (QName)
/** Constructs a node test of type Namespace. */
public NodeTest(String prefix, String uri) {
this.type = NAMESPACE;
this.name.setValues(prefix, null, null, uri);
} // (String,String)
/** Copy constructor. */
public NodeTest(NodeTest nodeTest) {
type = nodeTest.type;
name.setValues(nodeTest.name);
} // (NodeTest)
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
switch (type) {
case QNAME: {
if (name.prefix.length() !=0) {
if (name.uri != null) {
return name.prefix+':'+name.localpart;
}
return "{"+name.uri+'}'+name.prefix+':'+name.localpart;
}
return name.localpart;
}
case NAMESPACE: {
if (name.prefix.length() !=0) {
if (name.uri != null) {
return name.prefix+":*";
}
return "{"+name.uri+'}'+name.prefix+":*";
}
return "???:*";
}
case WILDCARD: {
return "*";
}
case NODE: {
return "node()";
}
}
return "???";
} // toString():String
/** Returns a clone of this object. */
public Object clone() {
return new NodeTest(this);
} // clone():Object
} // class NodeTest
// xpath implementation
// NOTE: The XPath implementation classes are kept internal because
// this implementation is just a temporary hack until a better
// and/or more appropriate implementation can be written.
// keeping the code in separate source files would "muddy" the
// CVS directory when it's not needed. -Ac
/**
* List of tokens.
*
* @xerces.internal
*
* @author Glenn Marcy, IBM
* @author Andy Clark, IBM
*
* @version $Id: XPath.java 965250 2010-07-18 16:04:58Z mrglavas $
*/
private static final class Tokens {
static final boolean DUMP_TOKENS = false;
/**
* [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
* | NameTest | NodeType | Operator | FunctionName
* | AxisName | Literal | Number | VariableReference
*/
public static final int
EXPRTOKEN_OPEN_PAREN = 0,
EXPRTOKEN_CLOSE_PAREN = 1,
EXPRTOKEN_OPEN_BRACKET = 2,
EXPRTOKEN_CLOSE_BRACKET = 3,
EXPRTOKEN_PERIOD = 4,
EXPRTOKEN_DOUBLE_PERIOD = 5,
EXPRTOKEN_ATSIGN = 6,
EXPRTOKEN_COMMA = 7,
EXPRTOKEN_DOUBLE_COLON = 8,
//
// [37] NameTest ::= '*' | NCName ':' '*' | QName
//
// followed by symbol handle of NCName or QName
//
EXPRTOKEN_NAMETEST_ANY = 9,
EXPRTOKEN_NAMETEST_NAMESPACE = 10,
EXPRTOKEN_NAMETEST_QNAME = 11,
//
// [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
//
EXPRTOKEN_NODETYPE_COMMENT = 12,
EXPRTOKEN_NODETYPE_TEXT = 13,
EXPRTOKEN_NODETYPE_PI = 14,
EXPRTOKEN_NODETYPE_NODE = 15,
//
// [32] Operator ::= OperatorName
// | MultiplyOperator
// | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
// [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
// [34] MultiplyOperator ::= '*'
//
EXPRTOKEN_OPERATOR_AND = 16,
EXPRTOKEN_OPERATOR_OR = 17,
EXPRTOKEN_OPERATOR_MOD = 18,
EXPRTOKEN_OPERATOR_DIV = 19,
EXPRTOKEN_OPERATOR_MULT = 20,
EXPRTOKEN_OPERATOR_SLASH = 21,
EXPRTOKEN_OPERATOR_DOUBLE_SLASH = 22,
EXPRTOKEN_OPERATOR_UNION = 23,
EXPRTOKEN_OPERATOR_PLUS = 24,
EXPRTOKEN_OPERATOR_MINUS = 25,
EXPRTOKEN_OPERATOR_EQUAL = 26,
EXPRTOKEN_OPERATOR_NOT_EQUAL = 27,
EXPRTOKEN_OPERATOR_LESS = 28,
EXPRTOKEN_OPERATOR_LESS_EQUAL = 29,
EXPRTOKEN_OPERATOR_GREATER = 30,
EXPRTOKEN_OPERATOR_GREATER_EQUAL = 31,
//EXPRTOKEN_FIRST_OPERATOR = EXPRTOKEN_OPERATOR_AND,
//EXPRTOKEN_LAST_OPERATOR = EXPRTOKEN_OPERATOR_GREATER_EQUAL,
//
// [35] FunctionName ::= QName - NodeType
//
// followed by symbol handle
//
EXPRTOKEN_FUNCTION_NAME = 32,
//
// [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
// | 'attribute'
// | 'child'
// | 'descendant' | 'descendant-or-self'
// | 'following' | 'following-sibling'
// | 'namespace'
// | 'parent'
// | 'preceding' | 'preceding-sibling'
// | 'self'
//
EXPRTOKEN_AXISNAME_ANCESTOR = 33,
EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF = 34,
EXPRTOKEN_AXISNAME_ATTRIBUTE = 35,
EXPRTOKEN_AXISNAME_CHILD = 36,
EXPRTOKEN_AXISNAME_DESCENDANT = 37,
EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF = 38,
EXPRTOKEN_AXISNAME_FOLLOWING = 39,
EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING = 40,
EXPRTOKEN_AXISNAME_NAMESPACE = 41,
EXPRTOKEN_AXISNAME_PARENT = 42,
EXPRTOKEN_AXISNAME_PRECEDING = 43,
EXPRTOKEN_AXISNAME_PRECEDING_SIBLING = 44,
EXPRTOKEN_AXISNAME_SELF = 45,
//
// [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
//
// followed by symbol handle for literal
//
EXPRTOKEN_LITERAL = 46,
//
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
// [31] Digits ::= [0-9]+
//
// followed by number handle
//
EXPRTOKEN_NUMBER = 47,
//
// [36] VariableReference ::= '$' QName
//
// followed by symbol handle for QName
//
EXPRTOKEN_VARIABLE_REFERENCE = 48;
private static final String[] fgTokenNames = {
"EXPRTOKEN_OPEN_PAREN",
"EXPRTOKEN_CLOSE_PAREN",
"EXPRTOKEN_OPEN_BRACKET",
"EXPRTOKEN_CLOSE_BRACKET",
"EXPRTOKEN_PERIOD",
"EXPRTOKEN_DOUBLE_PERIOD",
"EXPRTOKEN_ATSIGN",
"EXPRTOKEN_COMMA",
"EXPRTOKEN_DOUBLE_COLON",
"EXPRTOKEN_NAMETEST_ANY",
"EXPRTOKEN_NAMETEST_NAMESPACE",
"EXPRTOKEN_NAMETEST_QNAME",
"EXPRTOKEN_NODETYPE_COMMENT",
"EXPRTOKEN_NODETYPE_TEXT",
"EXPRTOKEN_NODETYPE_PI",
"EXPRTOKEN_NODETYPE_NODE",
"EXPRTOKEN_OPERATOR_AND",
"EXPRTOKEN_OPERATOR_OR",
"EXPRTOKEN_OPERATOR_MOD",
"EXPRTOKEN_OPERATOR_DIV",
"EXPRTOKEN_OPERATOR_MULT",
"EXPRTOKEN_OPERATOR_SLASH",
"EXPRTOKEN_OPERATOR_DOUBLE_SLASH",
"EXPRTOKEN_OPERATOR_UNION",
"EXPRTOKEN_OPERATOR_PLUS",
"EXPRTOKEN_OPERATOR_MINUS",
"EXPRTOKEN_OPERATOR_EQUAL",
"EXPRTOKEN_OPERATOR_NOT_EQUAL",
"EXPRTOKEN_OPERATOR_LESS",
"EXPRTOKEN_OPERATOR_LESS_EQUAL",
"EXPRTOKEN_OPERATOR_GREATER",
"EXPRTOKEN_OPERATOR_GREATER_EQUAL",
"EXPRTOKEN_FUNCTION_NAME",
"EXPRTOKEN_AXISNAME_ANCESTOR",
"EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF",
"EXPRTOKEN_AXISNAME_ATTRIBUTE",
"EXPRTOKEN_AXISNAME_CHILD",
"EXPRTOKEN_AXISNAME_DESCENDANT",
"EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF",
"EXPRTOKEN_AXISNAME_FOLLOWING",
"EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING",
"EXPRTOKEN_AXISNAME_NAMESPACE",
"EXPRTOKEN_AXISNAME_PARENT",
"EXPRTOKEN_AXISNAME_PRECEDING",
"EXPRTOKEN_AXISNAME_PRECEDING_SIBLING",
"EXPRTOKEN_AXISNAME_SELF",
"EXPRTOKEN_LITERAL",
"EXPRTOKEN_NUMBER",
"EXPRTOKEN_VARIABLE_REFERENCE"
};
/**
*
*/
private static final int INITIAL_TOKEN_COUNT = 1 << 8;
private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
private int fTokenCount = 0; // for writing
private SymbolTable fSymbolTable;
// REVISIT: Code something better here. -Ac
private java.util.Hashtable fSymbolMapping = new java.util.Hashtable();
// REVISIT: Code something better here. -Ac
private java.util.Hashtable fTokenNames = new java.util.Hashtable();
/**
* Current position in the token list.
*/
private int fCurrentTokenIndex;
//
// Constructors
//
public Tokens(SymbolTable symbolTable) {
fSymbolTable = symbolTable;
final String[] symbols = {
"ancestor", "ancestor-or-self", "attribute",
"child", "descendant", "descendant-or-self",
"following", "following-sibling", "namespace",
"parent", "preceding", "preceding-sibling",
"self",
};
for (int i = 0; i < symbols.length; i++) {
fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]), new Integer(i));
}
fTokenNames.put(new Integer(EXPRTOKEN_OPEN_PAREN), "EXPRTOKEN_OPEN_PAREN");
fTokenNames.put(new Integer(EXPRTOKEN_CLOSE_PAREN), "EXPRTOKEN_CLOSE_PAREN");
fTokenNames.put(new Integer(EXPRTOKEN_OPEN_BRACKET), "EXPRTOKEN_OPEN_BRACKET");
fTokenNames.put(new Integer(EXPRTOKEN_CLOSE_BRACKET), "EXPRTOKEN_CLOSE_BRACKET");
fTokenNames.put(new Integer(EXPRTOKEN_PERIOD), "EXPRTOKEN_PERIOD");
fTokenNames.put(new Integer(EXPRTOKEN_DOUBLE_PERIOD), "EXPRTOKEN_DOUBLE_PERIOD");
fTokenNames.put(new Integer(EXPRTOKEN_ATSIGN), "EXPRTOKEN_ATSIGN");
fTokenNames.put(new Integer(EXPRTOKEN_COMMA), "EXPRTOKEN_COMMA");
fTokenNames.put(new Integer(EXPRTOKEN_DOUBLE_COLON), "EXPRTOKEN_DOUBLE_COLON");
fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_ANY), "EXPRTOKEN_NAMETEST_ANY");
fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_NAMESPACE), "EXPRTOKEN_NAMETEST_NAMESPACE");
fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_QNAME), "EXPRTOKEN_NAMETEST_QNAME");
fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_COMMENT), "EXPRTOKEN_NODETYPE_COMMENT");
fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_TEXT), "EXPRTOKEN_NODETYPE_TEXT");
fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_PI), "EXPRTOKEN_NODETYPE_PI");
fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_NODE), "EXPRTOKEN_NODETYPE_NODE");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_AND), "EXPRTOKEN_OPERATOR_AND");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_OR), "EXPRTOKEN_OPERATOR_OR");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MOD), "EXPRTOKEN_OPERATOR_MOD");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_DIV), "EXPRTOKEN_OPERATOR_DIV");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MULT), "EXPRTOKEN_OPERATOR_MULT");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_SLASH), "EXPRTOKEN_OPERATOR_SLASH");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_DOUBLE_SLASH), "EXPRTOKEN_OPERATOR_DOUBLE_SLASH");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_UNION), "EXPRTOKEN_OPERATOR_UNION");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_PLUS), "EXPRTOKEN_OPERATOR_PLUS");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MINUS), "EXPRTOKEN_OPERATOR_MINUS");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_EQUAL), "EXPRTOKEN_OPERATOR_EQUAL");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_NOT_EQUAL), "EXPRTOKEN_OPERATOR_NOT_EQUAL");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_LESS), "EXPRTOKEN_OPERATOR_LESS");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_LESS_EQUAL), "EXPRTOKEN_OPERATOR_LESS_EQUAL");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_GREATER), "EXPRTOKEN_OPERATOR_GREATER");
fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_GREATER_EQUAL), "EXPRTOKEN_OPERATOR_GREATER_EQUAL");
fTokenNames.put(new Integer(EXPRTOKEN_FUNCTION_NAME), "EXPRTOKEN_FUNCTION_NAME");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ANCESTOR), "EXPRTOKEN_AXISNAME_ANCESTOR");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF), "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ATTRIBUTE), "EXPRTOKEN_AXISNAME_ATTRIBUTE");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_CHILD), "EXPRTOKEN_AXISNAME_CHILD");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_DESCENDANT), "EXPRTOKEN_AXISNAME_DESCENDANT");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF), "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_FOLLOWING), "EXPRTOKEN_AXISNAME_FOLLOWING");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING), "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_NAMESPACE), "EXPRTOKEN_AXISNAME_NAMESPACE");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PARENT), "EXPRTOKEN_AXISNAME_PARENT");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PRECEDING), "EXPRTOKEN_AXISNAME_PRECEDING");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PRECEDING_SIBLING), "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING");
fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_SELF), "EXPRTOKEN_AXISNAME_SELF");
fTokenNames.put(new Integer(EXPRTOKEN_LITERAL), "EXPRTOKEN_LITERAL");
fTokenNames.put(new Integer(EXPRTOKEN_NUMBER), "EXPRTOKEN_NUMBER");
fTokenNames.put(new Integer(EXPRTOKEN_VARIABLE_REFERENCE), "EXPRTOKEN_VARIABLE_REFERENCE");
}
//
// Public methods
//
// public String getTokenName(int token) {
// if (token < 0 || token >= fgTokenNames.length)
// return null;
// return fgTokenNames[token];
// }
//
public String getTokenString(int token) {
return (String)fTokenNames.get(new Integer(token));
}
public void addToken(String tokenStr) {
Integer tokenInt = (Integer)fTokenNames.get(tokenStr);
if (tokenInt == null) {
tokenInt = new Integer(fTokenNames.size());
fTokenNames.put(tokenInt, tokenStr);
}
addToken(tokenInt.intValue());
}
public void addToken(int token) {
try {
fTokens[fTokenCount] = token;
} catch (ArrayIndexOutOfBoundsException ex) {
int[] oldList = fTokens;
fTokens = new int[fTokenCount << 1];
System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
fTokens[fTokenCount] = token;
}
fTokenCount++;
}
// public int getTokenCount() {
// return fTokenCount;
// }
// public int getToken(int tokenIndex) {
// return fTokens[tokenIndex];
// }
/**
* Resets the current position to the head of the token list.
*/
public void rewind() {
fCurrentTokenIndex=0;
}
/**
* Returns true if the {@link #getNextToken()} method
* returns a valid token.
*/
public boolean hasMore() {
return fCurrentTokenIndexnew XPathException("c-general-xpath");.
*/
public int nextToken() throws XPathException {
if( fCurrentTokenIndex==fTokenCount )
throw new XPathException("c-general-xpath");
return fTokens[fCurrentTokenIndex++];
}
/**
* Obtains the token at the current position, without advancing
* the current position.
*
* If there's no such next token, this method throws
* new XPathException("c-general-xpath");.
*/
public int peekToken() throws XPathException {
if( fCurrentTokenIndex==fTokenCount )
throw new XPathException("c-general-xpath");
return fTokens[fCurrentTokenIndex];
}
/**
* Obtains the token at the current position as a String.
*
* If there's no current token or if the current token
* is not a string token, this method throws
* new XPathException("c-general-xpath");.
*/
public String nextTokenAsString() throws XPathException {
String s = getTokenString(nextToken());
if(s==null) throw new XPathException("c-general-xpath");
return s;
}
public void dumpTokens() {
//if (DUMP_TOKENS) {
for (int i = 0; i < fTokenCount; i++) {
switch (fTokens[i]) {
case EXPRTOKEN_OPEN_PAREN:
System.out.print(" ");
break;
case EXPRTOKEN_CLOSE_PAREN:
System.out.print(" ");
break;
case EXPRTOKEN_OPEN_BRACKET:
System.out.print(" ");
break;
case EXPRTOKEN_CLOSE_BRACKET:
System.out.print(" ");
break;
case EXPRTOKEN_PERIOD:
System.out.print(" ");
break;
case EXPRTOKEN_DOUBLE_PERIOD:
System.out.print(" ");
break;
case EXPRTOKEN_ATSIGN:
System.out.print(" ");
break;
case EXPRTOKEN_COMMA:
System.out.print(" ");
break;
case EXPRTOKEN_DOUBLE_COLON:
System.out.print(" ");
break;
case EXPRTOKEN_NAMETEST_ANY:
System.out.print(" ");
break;
case EXPRTOKEN_NAMETEST_NAMESPACE:
System.out.print(" ");
break;
case EXPRTOKEN_NAMETEST_QNAME:
System.out.print(" ");
break;
case EXPRTOKEN_NODETYPE_COMMENT:
System.out.print(" ");
break;
case EXPRTOKEN_NODETYPE_TEXT:
System.out.print(" ");
break;
case EXPRTOKEN_NODETYPE_PI:
System.out.print(" ");
break;
case EXPRTOKEN_NODETYPE_NODE:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_AND:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_OR:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_MOD:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_DIV:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_MULT:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_SLASH:
System.out.print(" ");
if (i + 1 < fTokenCount) {
System.out.println();
System.out.print(" ");
}
break;
case EXPRTOKEN_OPERATOR_DOUBLE_SLASH:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_UNION:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_PLUS:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_MINUS:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_EQUAL:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_NOT_EQUAL:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_LESS:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_LESS_EQUAL:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_GREATER:
System.out.print(" ");
break;
case EXPRTOKEN_OPERATOR_GREATER_EQUAL:
System.out.print(" ");
break;
case EXPRTOKEN_FUNCTION_NAME:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_ANCESTOR:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_ATTRIBUTE:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_CHILD:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_DESCENDANT:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_FOLLOWING:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_NAMESPACE:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_PARENT:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_PRECEDING:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
System.out.print(" ");
break;
case EXPRTOKEN_AXISNAME_SELF:
System.out.print(" ");
break;
case EXPRTOKEN_LITERAL:
System.out.print(" ");
break;
case EXPRTOKEN_NUMBER:
System.out.print(" ");
break;
case EXPRTOKEN_VARIABLE_REFERENCE:
System.out.print(" ");
break;
default:
System.out.println("??/>");
}
}
System.out.println();
//}
}
} // class Tokens
/**
* @xerces.internal
*
* @author Glenn Marcy, IBM
* @author Andy Clark, IBM
*
* @version $Id: XPath.java 965250 2010-07-18 16:04:58Z mrglavas $
*/
private static class Scanner {
/**
* 7-bit ASCII subset
*
* 0 1 2 3 4 5 6 7 8 9 A B C D E F
* 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
* SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
* @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
* P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
* `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
* p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
*/
private static final byte
CHARTYPE_INVALID = 0, // invalid XML character
CHARTYPE_OTHER = 1, // not special - one of "#%&;?\^`{}~" or DEL
CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
CHARTYPE_EXCLAMATION = 3, // '!' (0x21)
CHARTYPE_QUOTE = 4, // '\"' or '\'' (0x22 and 0x27)
CHARTYPE_DOLLAR = 5, // '$' (0x24)
CHARTYPE_OPEN_PAREN = 6, // '(' (0x28)
CHARTYPE_CLOSE_PAREN = 7, // ')' (0x29)
CHARTYPE_STAR = 8, // '*' (0x2A)
CHARTYPE_PLUS = 9, // '+' (0x2B)
CHARTYPE_COMMA = 10, // ',' (0x2C)
CHARTYPE_MINUS = 11, // '-' (0x2D)
CHARTYPE_PERIOD = 12, // '.' (0x2E)
CHARTYPE_SLASH = 13, // '/' (0x2F)
CHARTYPE_DIGIT = 14, // '0'-'9' (0x30 to 0x39)
CHARTYPE_COLON = 15, // ':' (0x3A)
CHARTYPE_LESS = 16, // '<' (0x3C)
CHARTYPE_EQUAL = 17, // '=' (0x3D)
CHARTYPE_GREATER = 18, // '>' (0x3E)
CHARTYPE_ATSIGN = 19, // '@' (0x40)
CHARTYPE_LETTER = 20, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
CHARTYPE_OPEN_BRACKET = 21, // '[' (0x5B)
CHARTYPE_CLOSE_BRACKET = 22, // ']' (0x5D)
CHARTYPE_UNDERSCORE = 23, // '_' (0x5F)
CHARTYPE_UNION = 24, // '|' (0x7C)
CHARTYPE_NONASCII = 25; // Non-ASCII Unicode codepoint (>= 0x80)
private static final byte[] fASCIICharMap = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 3, 4, 1, 5, 1, 1, 4, 6, 7, 8, 9, 10, 11, 12, 13,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 1, 16, 17, 18, 1,
19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 1, 22, 1, 23,
1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1, 24, 1, 1, 1
};
/**
* Symbol literals
*/
//
// Data
//
/** Symbol table. */
private SymbolTable fSymbolTable;
// symbols
private static final String fAndSymbol = "and".intern();
private static final String fOrSymbol = "or".intern();
private static final String fModSymbol = "mod".intern();
private static final String fDivSymbol = "div".intern();
private static final String fCommentSymbol = "comment".intern();
private static final String fTextSymbol = "text".intern();
private static final String fPISymbol = "processing-instruction".intern();
private static final String fNodeSymbol = "node".intern();
private static final String fAncestorSymbol = "ancestor".intern();
private static final String fAncestorOrSelfSymbol = "ancestor-or-self".intern();
private static final String fAttributeSymbol = "attribute".intern();
private static final String fChildSymbol = "child".intern();
private static final String fDescendantSymbol = "descendant".intern();
private static final String fDescendantOrSelfSymbol = "descendant-or-self".intern();
private static final String fFollowingSymbol = "following".intern();
private static final String fFollowingSiblingSymbol = "following-sibling".intern();
private static final String fNamespaceSymbol = "namespace".intern();
private static final String fParentSymbol = "parent".intern();
private static final String fPrecedingSymbol = "preceding".intern();
private static final String fPrecedingSiblingSymbol = "preceding-sibling".intern();
private static final String fSelfSymbol = "self".intern();
//
// Constructors
//
/** Constructs an XPath expression scanner. */
public Scanner(SymbolTable symbolTable) {
// save pool and tokens
fSymbolTable = symbolTable;
} // (SymbolTable)
/**
*
*/
public boolean scanExpr(SymbolTable symbolTable,
XPath.Tokens tokens, String data,
int currentOffset, int endOffset)
throws XPathException {
int nameOffset;
String nameHandle, prefixHandle;
boolean starIsMultiplyOperator = false;
int ch;
while (true) {
if (currentOffset == endOffset) {
break;
}
ch = data.charAt(currentOffset);
//
// [39] ExprWhitespace ::= S
//
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
if (++currentOffset == endOffset) {
break;
}
ch = data.charAt(currentOffset);
}
if (currentOffset == endOffset) {
break;
}
//
// [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
// | NameTest | NodeType | Operator | FunctionName
// | AxisName | Literal | Number | VariableReference
//
byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch];
switch (chartype) {
case CHARTYPE_OPEN_PAREN: // '('
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_CLOSE_PAREN: // ')'
addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN);
starIsMultiplyOperator = true;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_OPEN_BRACKET: // '['
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_BRACKET);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_CLOSE_BRACKET: // ']'
addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET);
starIsMultiplyOperator = true;
if (++currentOffset == endOffset) {
break;
}
break;
//
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
// ^^^^^^^^^^
//
case CHARTYPE_PERIOD: // '.', '..' or '.' Digits
if (currentOffset + 1 == endOffset) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
starIsMultiplyOperator = true;
currentOffset++;
break;
}
ch = data.charAt(currentOffset + 1);
if (ch == '.') { // '..'
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD);
starIsMultiplyOperator = true;
currentOffset += 2;
} else if (ch >= '0' && ch <= '9') {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
starIsMultiplyOperator = true;
currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
} else if (ch == '/') {
addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
starIsMultiplyOperator = true;
currentOffset++;
} else if (ch == '|') {
addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
starIsMultiplyOperator = true;
currentOffset++;
break;
} else if (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
// this is legal if the next token is non-existent or |
do {
if (++currentOffset == endOffset) {
break;
}
ch = data.charAt(currentOffset);
} while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D);
if (currentOffset == endOffset || ch == '|') {
addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
starIsMultiplyOperator = true;
break;
}
throw new XPathException ("c-general-xpath");
} else { // '.'
throw new XPathException ("c-general-xpath");
}
if (currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_ATSIGN: // '@'
addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_COMMA: // ','
addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_COLON: // '::'
if (++currentOffset == endOffset) {
// System.out.println("abort 1a");
return false; // REVISIT
}
ch = data.charAt(currentOffset);
if (ch != ':') {
// System.out.println("abort 1b");
return false; // REVISIT
}
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_SLASH: // '/' and '//'
if (++currentOffset == endOffset) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
starIsMultiplyOperator = false;
break;
}
ch = data.charAt(currentOffset);
if (ch == '/') { // '//'
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
} else {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
starIsMultiplyOperator = false;
}
break;
case CHARTYPE_UNION: // '|'
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_UNION);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_PLUS: // '+'
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_MINUS: // '-'
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_EQUAL: // '='
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_EXCLAMATION: // '!='
if (++currentOffset == endOffset) {
// System.out.println("abort 2a");
return false; // REVISIT
}
ch = data.charAt(currentOffset);
if (ch != '=') {
// System.out.println("abort 2b");
return false; // REVISIT
}
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
case CHARTYPE_LESS: // '<' and '<='
if (++currentOffset == endOffset) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
starIsMultiplyOperator = false;
break;
}
ch = data.charAt(currentOffset);
if (ch == '=') { // '<='
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
} else {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
starIsMultiplyOperator = false;
}
break;
case CHARTYPE_GREATER: // '>' and '>='
if (++currentOffset == endOffset) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
starIsMultiplyOperator = false;
break;
}
ch = data.charAt(currentOffset);
if (ch == '=') { // '>='
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
} else {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
starIsMultiplyOperator = false;
}
break;
//
// [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
//
case CHARTYPE_QUOTE: // '\"' or '\''
int qchar = ch;
if (++currentOffset == endOffset) {
// System.out.println("abort 2c");
return false; // REVISIT
}
ch = data.charAt(currentOffset);
int litOffset = currentOffset;
while (ch != qchar) {
if (++currentOffset == endOffset) {
// System.out.println("abort 2d");
return false; // REVISIT
}
ch = data.charAt(currentOffset);
}
int litLength = currentOffset - litOffset;
addToken(tokens, XPath.Tokens.EXPRTOKEN_LITERAL);
starIsMultiplyOperator = true;
tokens.addToken(symbolTable.addSymbol(data.substring(litOffset, litOffset + litLength)));
if (++currentOffset == endOffset) {
break;
}
break;
//
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
// [31] Digits ::= [0-9]+
//
case CHARTYPE_DIGIT:
addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
starIsMultiplyOperator = true;
currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
break;
//
// [36] VariableReference ::= '$' QName
//
case CHARTYPE_DOLLAR:
if (++currentOffset == endOffset) {
// System.out.println("abort 3a");
return false; // REVISIT
}
nameOffset = currentOffset;
currentOffset = scanNCName(data, endOffset, currentOffset);
if (currentOffset == nameOffset) {
// System.out.println("abort 3b");
return false; // REVISIT
}
if (currentOffset < endOffset) {
ch = data.charAt(currentOffset);
}
else {
ch = -1;
}
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
if (ch != ':') {
prefixHandle = XMLSymbols.EMPTY_STRING;
} else {
prefixHandle = nameHandle;
if (++currentOffset == endOffset) {
// System.out.println("abort 4a");
return false; // REVISIT
}
nameOffset = currentOffset;
currentOffset = scanNCName(data, endOffset, currentOffset);
if (currentOffset == nameOffset) {
// System.out.println("abort 4b");
return false; // REVISIT
}
if (currentOffset < endOffset) {
ch = data.charAt(currentOffset);
}
else {
ch = -1;
}
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
}
addToken(tokens, XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE);
starIsMultiplyOperator = true;
tokens.addToken(prefixHandle);
tokens.addToken(nameHandle);
break;
//
// [37] NameTest ::= '*' | NCName ':' '*' | QName
// [34] MultiplyOperator ::= '*'
//
case CHARTYPE_STAR: // '*'
//
// 3.7 Lexical Structure
//
// If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
// an Operator, then a * must be recognized as a MultiplyOperator.
//
// Otherwise, the token must not be recognized as a MultiplyOperator.
//
if (starIsMultiplyOperator) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MULT);
starIsMultiplyOperator = false;
} else {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_ANY);
starIsMultiplyOperator = true;
}
if (++currentOffset == endOffset) {
break;
}
break;
//
// NCName, QName and non-terminals
//
case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic)
case CHARTYPE_LETTER:
case CHARTYPE_UNDERSCORE:
//
// 3.7 Lexical Structure
//
// If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
// an Operator, then an NCName must be recognized as an OperatorName.
//
// If the character following an NCName (possibly after intervening ExprWhitespace) is (,
// then the token must be recognized as a NodeType or a FunctionName.
//
// If the two characters following an NCName (possibly after intervening ExprWhitespace)
// are ::, then the token must be recognized as an AxisName.
//
// Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
// FunctionName, or an AxisName.
//
// [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
// [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
// [35] FunctionName ::= QName - NodeType
// [6] AxisName ::= (see above)
//
// [37] NameTest ::= '*' | NCName ':' '*' | QName
// [5] NCName ::= (Letter | '_') (NCNameChar)*
// [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar')
// [?] QName ::= (NCName ':')? NCName
// [?] Letter ::= [A-Za-z] (ascii subset of 'Letter')
// [?] Digit ::= [0-9] (ascii subset of 'Digit')
//
nameOffset = currentOffset;
currentOffset = scanNCName(data, endOffset, currentOffset);
if (currentOffset == nameOffset) {
// System.out.println("abort 4c");
return false; // REVISIT
}
if (currentOffset < endOffset) {
ch = data.charAt(currentOffset);
}
else {
ch = -1;
}
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
boolean isNameTestNCName = false;
boolean isAxisName = false;
prefixHandle = XMLSymbols.EMPTY_STRING;
if (ch == ':') {
if (++currentOffset == endOffset) {
// System.out.println("abort 5");
return false; // REVISIT
}
ch = data.charAt(currentOffset);
if (ch == '*') {
if (++currentOffset < endOffset) {
ch = data.charAt(currentOffset);
}
isNameTestNCName = true;
} else if (ch == ':') {
if (++currentOffset < endOffset) {
ch = data.charAt(currentOffset);
}
isAxisName = true;
} else {
prefixHandle = nameHandle;
nameOffset = currentOffset;
currentOffset = scanNCName(data, endOffset, currentOffset);
if (currentOffset == nameOffset) {
// System.out.println("abort 5b");
return false; // REVISIT
}
if (currentOffset < endOffset) {
ch = data.charAt(currentOffset);
}
else {
ch = -1;
}
nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset));
}
}
//
// [39] ExprWhitespace ::= S
//
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
if (++currentOffset == endOffset) {
break;
}
ch = data.charAt(currentOffset);
}
//
// If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
// an Operator, then an NCName must be recognized as an OperatorName.
//
if (starIsMultiplyOperator) {
if (nameHandle == fAndSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_AND);
starIsMultiplyOperator = false;
} else if (nameHandle == fOrSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_OR);
starIsMultiplyOperator = false;
} else if (nameHandle == fModSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MOD);
starIsMultiplyOperator = false;
} else if (nameHandle == fDivSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DIV);
starIsMultiplyOperator = false;
} else {
// System.out.println("abort 6");
return false; // REVISIT
}
if (isNameTestNCName) {
// System.out.println("abort 7");
return false; // REVISIT - NCName:* where an OperatorName is required
} else if (isAxisName) {
// System.out.println("abort 8");
return false; // REVISIT - AxisName:: where an OperatorName is required
}
break;
}
//
// If the character following an NCName (possibly after intervening ExprWhitespace) is (,
// then the token must be recognized as a NodeType or a FunctionName.
//
if (ch == '(' && !isNameTestNCName && !isAxisName) {
if (nameHandle == fCommentSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT);
} else if (nameHandle == fTextSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT);
} else if (nameHandle == fPISymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_PI);
} else if (nameHandle == fNodeSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_NODE);
} else {
addToken(tokens, XPath.Tokens.EXPRTOKEN_FUNCTION_NAME);
tokens.addToken(prefixHandle);
tokens.addToken(nameHandle);
}
addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
starIsMultiplyOperator = false;
if (++currentOffset == endOffset) {
break;
}
break;
}
//
// If the two characters following an NCName (possibly after intervening ExprWhitespace)
// are ::, then the token must be recognized as an AxisName.
//
if (isAxisName ||
(ch == ':' && currentOffset + 1 < endOffset &&
data.charAt(currentOffset + 1) == ':')) {
if (nameHandle == fAncestorSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR);
} else if (nameHandle == fAncestorOrSelfSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF);
} else if (nameHandle == fAttributeSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE);
} else if (nameHandle == fChildSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD);
} else if (nameHandle == fDescendantSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT);
} else if (nameHandle == fDescendantOrSelfSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF);
} else if (nameHandle == fFollowingSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING);
} else if (nameHandle == fFollowingSiblingSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING);
} else if (nameHandle == fNamespaceSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE);
} else if (nameHandle == fParentSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT);
} else if (nameHandle == fPrecedingSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING);
} else if (nameHandle == fPrecedingSiblingSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING);
} else if (nameHandle == fSelfSymbol) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_SELF);
} else {
// System.out.println("abort 9");
return false; // REVISIT
}
if (isNameTestNCName) {
// System.out.println("abort 10");
return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required
}
addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
starIsMultiplyOperator = false;
if (!isAxisName) {
currentOffset++;
if (++currentOffset == endOffset) {
break;
}
}
break;
}
//
// Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
// FunctionName, or an AxisName.
//
if (isNameTestNCName) {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE);
starIsMultiplyOperator = true;
tokens.addToken(nameHandle);
} else {
addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME);
starIsMultiplyOperator = true;
tokens.addToken(prefixHandle);
tokens.addToken(nameHandle);
}
break;
default:
// CHARTYPE_INVALID or CHARTYPE_OTHER
// We're not expecting to find either of these in a valid expression.
return false;
}
}
if (XPath.Tokens.DUMP_TOKENS) {
tokens.dumpTokens();
}
return true;
}
//
// [5] NCName ::= (Letter | '_') (NCNameChar)*
// [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
//
int scanNCName(String data, int endOffset, int currentOffset) {
int ch = data.charAt(currentOffset);
if (ch >= 0x80) {
if (!XMLChar.isNameStart(ch))
/*** // REVISIT: Make sure this is a negation. ***
if ((XMLCharacterProperties.fgCharFlags[ch] &
XMLCharacterProperties.E_InitialNameCharFlag) == 0)
/***/
{
return currentOffset;
}
}
else {
byte chartype = fASCIICharMap[ch];
if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
return currentOffset;
}
}
while (++currentOffset < endOffset) {
ch = data.charAt(currentOffset);
if (ch >= 0x80) {
if (!XMLChar.isName(ch))
/*** // REVISIT: Make sure this is a negation. ***
if ((XMLCharacterProperties.fgCharFlags[ch] &
XMLCharacterProperties.E_NameCharFlag) == 0)
/***/
{
break;
}
}
else {
byte chartype = fASCIICharMap[ch];
if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
chartype != CHARTYPE_UNDERSCORE)
{
break;
}
}
}
return currentOffset;
}
//
// [30] Number ::= Digits ('.' Digits?)? | '.' Digits
// [31] Digits ::= [0-9]+
//
private int scanNumber(XPath.Tokens tokens, String/*byte[]*/ data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) {
int ch = data.charAt(currentOffset);
int whole = 0;
int part = 0;
while (ch >= '0' && ch <= '9') {
whole = (whole * 10) + (ch - '0');
if (++currentOffset == endOffset) {
break;
}
ch = data.charAt(currentOffset);
}
if (ch == '.') {
if (++currentOffset < endOffset) {
/** int start = currentOffset; **/
ch = data.charAt(currentOffset);
while (ch >= '0' && ch <= '9') {
part = (part * 10) + (ch - '0');
if (++currentOffset == endOffset) {
break;
}
ch = data.charAt(currentOffset);
}
if (part != 0) {
/***
part = tokens.addSymbol(data, start, currentOffset - start, encoding);
/***/
throw new RuntimeException("find a solution!");
//part = fStringPool.addSymbol(data.substring(start, currentOffset));
/***/
}
}
}
tokens.addToken(whole);
tokens.addToken(part);
return currentOffset;
}
//
// Protected methods
//
/**
* This method adds the specified token to the token list. By
* default, this method allows all tokens. However, subclasses
* of the XPathExprScanner can override this method in order
* to disallow certain tokens from being used in the scanned
* XPath expression. This is a convenient way of allowing only
* a subset of XPath.
*/
protected void addToken(XPath.Tokens tokens, int token)
throws XPathException {
tokens.addToken(token);
} // addToken(int)
} // class Scanner
//
// MAIN
//
/** Main program entry. */
public static void main(String[] argv) throws Exception {
for (int i = 0; i < argv.length; i++) {
final String expression = argv[i];
System.out.println("# XPath expression: \""+expression+'"');
try {
SymbolTable symbolTable = new SymbolTable();
XPath xpath = new XPath(expression, symbolTable, null);
System.out.println("expanded xpath: \""+xpath.toString()+'"');
}
catch (XPathException e) {
System.out.println("error: "+e.getMessage());
}
}
} // main(String[])
} // class XPath