0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.xerces.impl.xpath;
0019:
0020: import java.util.ArrayList;
0021: import java.util.Vector;
0022:
0023: import org.apache.xerces.util.SymbolTable;
0024: import org.apache.xerces.util.XMLChar;
0025: import org.apache.xerces.util.XMLSymbols;
0026: import org.apache.xerces.xni.NamespaceContext;
0027: import org.apache.xerces.xni.QName;
0028:
0029: /**
0030: * Bare minimum XPath parser.
0031: *
0032: * @xerces.internal
0033: *
0034: * @author Andy Clark, IBM
0035: * @version $Id: XPath.java 572107 2007-09-02 18:40:40Z mrglavas $
0036: */
0037: public class XPath {
0038:
0039: //
0040: // Constants
0041: //
0042:
0043: private static final boolean DEBUG_ALL = false;
0044:
0045: private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false;
0046:
0047: private static final boolean DEBUG_ANY = DEBUG_XPATH_PARSE;
0048:
0049: //
0050: // Data
0051: //
0052:
0053: /** Expression. */
0054: protected final String fExpression;
0055:
0056: /** Symbol table. */
0057: protected final SymbolTable fSymbolTable;
0058:
0059: /** Location paths. */
0060: protected final LocationPath[] fLocationPaths;
0061:
0062: //
0063: // Constructors
0064: //
0065:
0066: /** Constructs an XPath object from the specified expression. */
0067: public XPath(String xpath, SymbolTable symbolTable,
0068: NamespaceContext context) throws XPathException {
0069: fExpression = xpath;
0070: fSymbolTable = symbolTable;
0071: fLocationPaths = parseExpression(context);
0072: if (DEBUG_XPATH_PARSE) {
0073: System.out.println(">>> " + fLocationPaths);
0074: }
0075: } // <init>(String,SymbolTable,NamespaceContext)
0076:
0077: //
0078: // Public methods
0079: //
0080:
0081: /**
0082: * Returns a representation of all location paths for this XPath.
0083: * XPath = locationPath ( '|' locationPath)
0084: */
0085: public LocationPath[] getLocationPaths() {
0086: LocationPath[] ret = new LocationPath[fLocationPaths.length];
0087: for (int i = 0; i < fLocationPaths.length; i++) {
0088: ret[i] = (LocationPath) fLocationPaths[i].clone();
0089: }
0090: return ret;
0091: } // getLocationPath(LocationPath)
0092:
0093: /** Returns a representation of the first location path for this XPath. */
0094: public LocationPath getLocationPath() {
0095: return (LocationPath) fLocationPaths[0].clone();
0096: } // getLocationPath(LocationPath)
0097:
0098: //
0099: // Object methods
0100: //
0101:
0102: /** Returns a string representation of this object. */
0103: public String toString() {
0104: StringBuffer buf = new StringBuffer();
0105: for (int i = 0; i < fLocationPaths.length; i++) {
0106: if (i > 0) {
0107: buf.append("|");
0108: }
0109: buf.append(fLocationPaths[i].toString());
0110: }
0111: return buf.toString();
0112: } // toString():String
0113:
0114: //
0115: // Private methods
0116: //
0117:
0118: /**
0119: * Used by the {@link #parseExpression(NamespaceContext)} method
0120: * to verify the assumption.
0121: *
0122: * If <tt>b</tt> is false, this method throws XPathException
0123: * to report the error.
0124: */
0125: private static void check(boolean b) throws XPathException {
0126: if (!b)
0127: throw new XPathException("c-general-xpath");
0128: }
0129:
0130: /**
0131: * Used by the {@link #parseExpression(NamespaceContext)} method
0132: * to build a {@link LocationPath} object from the accumulated
0133: * {@link Step}s.
0134: */
0135: private LocationPath buildLocationPath(Vector stepsVector)
0136: throws XPathException {
0137: int size = stepsVector.size();
0138: check(size != 0);
0139: Step[] steps = new Step[size];
0140: stepsVector.copyInto(steps);
0141: stepsVector.removeAllElements();
0142:
0143: return new LocationPath(steps);
0144: }
0145:
0146: /**
0147: * This method is implemented by using the XPathExprScanner and
0148: * examining the list of tokens that it returns.
0149: */
0150: private LocationPath[] parseExpression(
0151: final NamespaceContext context) throws XPathException {
0152:
0153: // tokens
0154: final XPath.Tokens xtokens = new XPath.Tokens(fSymbolTable);
0155:
0156: // scanner
0157: XPath.Scanner scanner = new XPath.Scanner(fSymbolTable) {
0158: protected void addToken(XPath.Tokens tokens, int token)
0159: throws XPathException {
0160: if (token == XPath.Tokens.EXPRTOKEN_ATSIGN
0161: || token == XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME
0162: || token == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH
0163: || token == XPath.Tokens.EXPRTOKEN_PERIOD
0164: || token == XPath.Tokens.EXPRTOKEN_NAMETEST_ANY
0165: || token == XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE
0166: || token == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH
0167: || token == XPath.Tokens.EXPRTOKEN_OPERATOR_UNION
0168: || token == XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD
0169: || token == XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE
0170: || token == XPath.Tokens.EXPRTOKEN_DOUBLE_COLON
0171: //
0172: ) {
0173: super .addToken(tokens, token);
0174: return;
0175: }
0176: throw new XPathException("c-general-xpath");
0177: }
0178: };
0179:
0180: int length = fExpression.length();
0181:
0182: boolean success = scanner.scanExpr(fSymbolTable, xtokens,
0183: fExpression, 0, length);
0184: if (!success)
0185: throw new XPathException("c-general-xpath");
0186:
0187: //fTokens.dumpTokens();
0188: Vector stepsVector = new Vector();
0189: ArrayList locationPathsVector = new ArrayList();
0190:
0191: // true when the next token should be 'Step' (as defined in
0192: // the production rule [3] of XML Schema P1 section 3.11.6
0193: // if false, we are expecting either '|' or '/'.
0194: //
0195: // this is to make sure we can detect a token list like
0196: // 'abc' '/' '/' 'def' 'ghi'
0197: boolean expectingStep = true;
0198:
0199: while (xtokens.hasMore()) {
0200: final int token = xtokens.nextToken();
0201:
0202: switch (token) {
0203: case XPath.Tokens.EXPRTOKEN_OPERATOR_UNION: {
0204: check(!expectingStep);
0205: locationPathsVector.add(buildLocationPath(stepsVector));
0206: expectingStep = true;
0207: break;
0208: }
0209: case XPath.Tokens.EXPRTOKEN_ATSIGN: {
0210: check(expectingStep);
0211: Step step = new Step(new Axis(Axis.ATTRIBUTE),
0212: parseNodeTest(xtokens.nextToken(), xtokens,
0213: context));
0214: stepsVector.addElement(step);
0215: expectingStep = false;
0216: break;
0217: }
0218: case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: {
0219: check(expectingStep);
0220: // If we got here we're expecting attribute::
0221: if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
0222: throw new XPathException("c-general-xpath");
0223: }
0224: Step step = new Step(new Axis(Axis.ATTRIBUTE),
0225: parseNodeTest(xtokens.nextToken(), xtokens,
0226: context));
0227: stepsVector.addElement(step);
0228: expectingStep = false;
0229: break;
0230: }
0231: case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
0232: case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
0233: case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: {
0234: check(expectingStep);
0235: Step step = new Step(new Axis(Axis.CHILD),
0236: parseNodeTest(token, xtokens, context));
0237: stepsVector.addElement(step);
0238: expectingStep = false;
0239: break;
0240: }
0241: case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD: {
0242: check(expectingStep);
0243: // If we got here we're expecting child::
0244: if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
0245: throw new XPathException("c-general-xpath");
0246: }
0247: Step step = new Step(new Axis(Axis.CHILD),
0248: parseNodeTest(xtokens.nextToken(), xtokens,
0249: context));
0250: stepsVector.addElement(step);
0251: expectingStep = false;
0252: break;
0253: }
0254: case XPath.Tokens.EXPRTOKEN_PERIOD: {
0255: check(expectingStep);
0256: expectingStep = false;
0257:
0258: // unless this is the first step in this location path,
0259: // there's really no reason to keep them in LocationPath.
0260: // This amounts to shorten "a/././b/./c" to "a/b/c".
0261: // Also, the matcher fails to work correctly if XPath
0262: // has those redundant dots.
0263: if (stepsVector.size() == 0) {
0264: // build step
0265: Axis axis = new Axis(Axis.SELF);
0266: NodeTest nodeTest = new NodeTest(NodeTest.NODE);
0267: Step step = new Step(axis, nodeTest);
0268: stepsVector.addElement(step);
0269:
0270: if (xtokens.hasMore()
0271: && xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH) {
0272: // consume '//'
0273: xtokens.nextToken();
0274:
0275: // build step
0276: axis = new Axis(Axis.DESCENDANT);
0277: nodeTest = new NodeTest(NodeTest.NODE);
0278: step = new Step(axis, nodeTest);
0279: stepsVector.addElement(step);
0280: expectingStep = true;
0281: }
0282: }
0283: break;
0284: }
0285: case XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH: {
0286: // this cannot appear in an arbitrary position.
0287: // it is only allowed right after '.' when
0288: // '.' is the first token of a location path.
0289: throw new XPathException("c-general-xpath");
0290: }
0291: case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON: {
0292: // :: cannot appear in an arbitrary position.
0293: // We only expect this token if the xpath
0294: // contains child:: or attribute::
0295: throw new XPathException("c-general-xpath");
0296: }
0297: case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: {
0298: check(!expectingStep);
0299: expectingStep = true;
0300: break;
0301: }
0302: default:
0303: // we should have covered all the tokens that we can possibly see.
0304: throw new InternalError();
0305: }
0306: }
0307:
0308: check(!expectingStep);
0309:
0310: locationPathsVector.add(buildLocationPath(stepsVector));
0311:
0312: // return location path
0313: return (LocationPath[]) locationPathsVector
0314: .toArray(new LocationPath[locationPathsVector.size()]);
0315:
0316: } // parseExpression(SymbolTable,NamespaceContext)
0317:
0318: /**
0319: * Used by {@link #parseExpression} to parse a node test
0320: * from the token list.
0321: */
0322: private NodeTest parseNodeTest(int typeToken, Tokens xtokens,
0323: NamespaceContext context) throws XPathException {
0324: switch (typeToken) {
0325: case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
0326: return new NodeTest(NodeTest.WILDCARD);
0327:
0328: case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
0329: case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME:
0330: // consume QName token
0331: String prefix = xtokens.nextTokenAsString();
0332: String uri = null;
0333: if (context != null && prefix != XMLSymbols.EMPTY_STRING) {
0334: uri = context.getURI(prefix);
0335: }
0336: if (prefix != XMLSymbols.EMPTY_STRING && context != null
0337: && uri == null) {
0338: throw new XPathException("c-general-xpath-ns");
0339: }
0340:
0341: if (typeToken == XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE)
0342: return new NodeTest(prefix, uri);
0343:
0344: String localpart = xtokens.nextTokenAsString();
0345: String rawname = prefix != XMLSymbols.EMPTY_STRING ? fSymbolTable
0346: .addSymbol(prefix + ':' + localpart)
0347: : localpart;
0348:
0349: return new NodeTest(new QName(prefix, localpart, rawname,
0350: uri));
0351:
0352: default:
0353: throw new XPathException("c-general-xpath");
0354: }
0355: }
0356:
0357: //
0358: // Classes
0359: //
0360:
0361: // location path information
0362:
0363: /**
0364: * A location path representation for an XPath expression.
0365: *
0366: * @xerces.internal
0367: *
0368: * @author Andy Clark, IBM
0369: */
0370: public static class LocationPath implements Cloneable {
0371:
0372: //
0373: // Data
0374: //
0375:
0376: /** List of steps. */
0377: public final Step[] steps;
0378:
0379: //
0380: // Constructors
0381: //
0382:
0383: /** Creates a location path from a series of steps. */
0384: public LocationPath(Step[] steps) {
0385: this .steps = steps;
0386: } // <init>(Step[])
0387:
0388: /** Copy constructor. */
0389: protected LocationPath(LocationPath path) {
0390: steps = new Step[path.steps.length];
0391: for (int i = 0; i < steps.length; i++) {
0392: steps[i] = (Step) path.steps[i].clone();
0393: }
0394: } // <init>(LocationPath)
0395:
0396: //
0397: // Object methods
0398: //
0399:
0400: /** Returns a string representation of this object. */
0401: public String toString() {
0402: StringBuffer str = new StringBuffer();
0403: for (int i = 0; i < steps.length; i++) {
0404: if (i > 0
0405: && (steps[i - 1].axis.type != Axis.DESCENDANT && steps[i].axis.type != Axis.DESCENDANT)) {
0406: str.append('/');
0407: }
0408: str.append(steps[i].toString());
0409: }
0410: // DEBUG: This code is just for debugging and should *not*
0411: // be left in because it will mess up hashcodes of
0412: // serialized versions of this object. -Ac
0413: if (false) {
0414: str.append('[');
0415: String s = super .toString();
0416: str.append(s.substring(s.indexOf('@')));
0417: str.append(']');
0418: }
0419: return str.toString();
0420: } // toString():String
0421:
0422: /** Returns a clone of this object. */
0423: public Object clone() {
0424: return new LocationPath(this );
0425: } // clone():Object
0426:
0427: } // class locationPath
0428:
0429: /**
0430: * A location path step comprised of an axis and node test.
0431: *
0432: * @xerces.internal
0433: *
0434: * @author Andy Clark, IBM
0435: */
0436: public static class Step implements Cloneable {
0437:
0438: //
0439: // Data
0440: //
0441:
0442: /** Axis. */
0443: public final Axis axis;
0444:
0445: /** Node test. */
0446: public final NodeTest nodeTest;
0447:
0448: //
0449: // Constructors
0450: //
0451:
0452: /** Constructs a step from an axis and node test. */
0453: public Step(Axis axis, NodeTest nodeTest) {
0454: this .axis = axis;
0455: this .nodeTest = nodeTest;
0456: } // <init>(Axis,NodeTest)
0457:
0458: /** Copy constructor. */
0459: protected Step(Step step) {
0460: axis = (Axis) step.axis.clone();
0461: nodeTest = (NodeTest) step.nodeTest.clone();
0462: } // <init>(Step)
0463:
0464: //
0465: // Object methods
0466: //
0467:
0468: /** Returns a string representation of this object. */
0469: public String toString() {
0470: if (axis.type == Axis.SELF) {
0471: return ".";
0472: }
0473: if (axis.type == Axis.ATTRIBUTE) {
0474: return "@" + nodeTest.toString();
0475: }
0476: if (axis.type == Axis.CHILD) {
0477: return nodeTest.toString();
0478: }
0479: if (axis.type == Axis.DESCENDANT) {
0480: return "//";
0481: }
0482: return "??? (" + axis.type + ')';
0483: } // toString():String
0484:
0485: /** Returns a clone of this object. */
0486: public Object clone() {
0487: return new Step(this );
0488: } // clone():Object
0489:
0490: } // class Step
0491:
0492: /**
0493: * Axis.
0494: *
0495: * @xerces.internal
0496: *
0497: * @author Andy Clark, IBM
0498: */
0499: public static class Axis implements Cloneable {
0500:
0501: //
0502: // Constants
0503: //
0504:
0505: /** Type: child. */
0506: public static final short CHILD = 1;
0507:
0508: /** Type: attribute. */
0509: public static final short ATTRIBUTE = 2;
0510:
0511: /** Type: self. */
0512: public static final short SELF = 3;
0513:
0514: /** Type: descendant. */
0515: public static final short DESCENDANT = 4;
0516: //
0517: // Data
0518: //
0519:
0520: /** Axis type. */
0521: public final short type;
0522:
0523: //
0524: // Constructors
0525: //
0526:
0527: /** Constructs an axis with the specified type. */
0528: public Axis(short type) {
0529: this .type = type;
0530: } // <init>(short)
0531:
0532: /** Copy constructor. */
0533: protected Axis(Axis axis) {
0534: type = axis.type;
0535: } // <init>(Axis)
0536:
0537: //
0538: // Object methods
0539: //
0540:
0541: /** Returns a string representation of this object. */
0542: public String toString() {
0543: switch (type) {
0544: case CHILD:
0545: return "child";
0546: case ATTRIBUTE:
0547: return "attribute";
0548: case SELF:
0549: return "self";
0550: case DESCENDANT:
0551: return "descendant";
0552: }
0553: return "???";
0554: } // toString():String
0555:
0556: /** Returns a clone of this object. */
0557: public Object clone() {
0558: return new Axis(this );
0559: } // clone():Object
0560:
0561: } // class Axis
0562:
0563: /**
0564: * Node test.
0565: *
0566: * @xerces.internal
0567: *
0568: * @author Andy Clark, IBM
0569: */
0570: public static class NodeTest implements Cloneable {
0571:
0572: //
0573: // Constants
0574: //
0575:
0576: /** Type: qualified name. */
0577: public static final short QNAME = 1;
0578:
0579: /** Type: wildcard. */
0580: public static final short WILDCARD = 2;
0581:
0582: /** Type: node. */
0583: public static final short NODE = 3;
0584:
0585: /** Type: namespace */
0586: public static final short NAMESPACE = 4;
0587:
0588: //
0589: // Data
0590: //
0591:
0592: /** Node test type. */
0593: public final short type;
0594:
0595: /** Node qualified name. */
0596: public final QName name = new QName();
0597:
0598: //
0599: // Constructors
0600: //
0601:
0602: /** Constructs a node test of type WILDCARD or NODE. */
0603: public NodeTest(short type) {
0604: this .type = type;
0605: } // <init>(int)
0606:
0607: /** Constructs a node test of type QName. */
0608: public NodeTest(QName name) {
0609: this .type = QNAME;
0610: this .name.setValues(name);
0611: } // <init>(QName)
0612:
0613: /** Constructs a node test of type Namespace. */
0614: public NodeTest(String prefix, String uri) {
0615: this .type = NAMESPACE;
0616: this .name.setValues(prefix, null, null, uri);
0617: } // <init>(String,String)
0618:
0619: /** Copy constructor. */
0620: public NodeTest(NodeTest nodeTest) {
0621: type = nodeTest.type;
0622: name.setValues(nodeTest.name);
0623: } // <init>(NodeTest)
0624:
0625: //
0626: // Object methods
0627: //
0628:
0629: /** Returns a string representation of this object. */
0630: public String toString() {
0631:
0632: switch (type) {
0633: case QNAME: {
0634: if (name.prefix.length() != 0) {
0635: if (name.uri != null) {
0636: return name.prefix + ':' + name.localpart;
0637: }
0638: return "{" + name.uri + '}' + name.prefix + ':'
0639: + name.localpart;
0640: }
0641: return name.localpart;
0642: }
0643: case NAMESPACE: {
0644: if (name.prefix.length() != 0) {
0645: if (name.uri != null) {
0646: return name.prefix + ":*";
0647: }
0648: return "{" + name.uri + '}' + name.prefix + ":*";
0649: }
0650: return "???:*";
0651: }
0652: case WILDCARD: {
0653: return "*";
0654: }
0655: case NODE: {
0656: return "node()";
0657: }
0658: }
0659: return "???";
0660:
0661: } // toString():String
0662:
0663: /** Returns a clone of this object. */
0664: public Object clone() {
0665: return new NodeTest(this );
0666: } // clone():Object
0667:
0668: } // class NodeTest
0669:
0670: // xpath implementation
0671:
0672: // NOTE: The XPath implementation classes are kept internal because
0673: // this implementation is just a temporary hack until a better
0674: // and/or more appropriate implementation can be written.
0675: // keeping the code in separate source files would "muddy" the
0676: // CVS directory when it's not needed. -Ac
0677:
0678: /**
0679: * List of tokens.
0680: *
0681: * @xerces.internal
0682: *
0683: * @author Glenn Marcy, IBM
0684: * @author Andy Clark, IBM
0685: *
0686: * @version $Id: XPath.java 572107 2007-09-02 18:40:40Z mrglavas $
0687: */
0688: private static final class Tokens {
0689:
0690: static final boolean DUMP_TOKENS = false;
0691:
0692: /**
0693: * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
0694: * | NameTest | NodeType | Operator | FunctionName
0695: * | AxisName | Literal | Number | VariableReference
0696: */
0697: public static final int EXPRTOKEN_OPEN_PAREN = 0,
0698: EXPRTOKEN_CLOSE_PAREN = 1,
0699: EXPRTOKEN_OPEN_BRACKET = 2,
0700: EXPRTOKEN_CLOSE_BRACKET = 3,
0701: EXPRTOKEN_PERIOD = 4,
0702: EXPRTOKEN_DOUBLE_PERIOD = 5,
0703: EXPRTOKEN_ATSIGN = 6,
0704: EXPRTOKEN_COMMA = 7,
0705: EXPRTOKEN_DOUBLE_COLON = 8,
0706: //
0707: // [37] NameTest ::= '*' | NCName ':' '*' | QName
0708: //
0709: // followed by symbol handle of NCName or QName
0710: //
0711: EXPRTOKEN_NAMETEST_ANY = 9,
0712: EXPRTOKEN_NAMETEST_NAMESPACE = 10,
0713: EXPRTOKEN_NAMETEST_QNAME = 11,
0714: //
0715: // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
0716: //
0717: EXPRTOKEN_NODETYPE_COMMENT = 12,
0718: EXPRTOKEN_NODETYPE_TEXT = 13,
0719: EXPRTOKEN_NODETYPE_PI = 14,
0720: EXPRTOKEN_NODETYPE_NODE = 15,
0721: //
0722: // [32] Operator ::= OperatorName
0723: // | MultiplyOperator
0724: // | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
0725: // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
0726: // [34] MultiplyOperator ::= '*'
0727: //
0728: EXPRTOKEN_OPERATOR_AND = 16,
0729: EXPRTOKEN_OPERATOR_OR = 17,
0730: EXPRTOKEN_OPERATOR_MOD = 18,
0731: EXPRTOKEN_OPERATOR_DIV = 19,
0732: EXPRTOKEN_OPERATOR_MULT = 20,
0733: EXPRTOKEN_OPERATOR_SLASH = 21,
0734: EXPRTOKEN_OPERATOR_DOUBLE_SLASH = 22,
0735: EXPRTOKEN_OPERATOR_UNION = 23,
0736: EXPRTOKEN_OPERATOR_PLUS = 24,
0737: EXPRTOKEN_OPERATOR_MINUS = 25,
0738: EXPRTOKEN_OPERATOR_EQUAL = 26,
0739: EXPRTOKEN_OPERATOR_NOT_EQUAL = 27,
0740: EXPRTOKEN_OPERATOR_LESS = 28,
0741: EXPRTOKEN_OPERATOR_LESS_EQUAL = 29,
0742: EXPRTOKEN_OPERATOR_GREATER = 30,
0743: EXPRTOKEN_OPERATOR_GREATER_EQUAL = 31,
0744:
0745: //EXPRTOKEN_FIRST_OPERATOR = EXPRTOKEN_OPERATOR_AND,
0746: //EXPRTOKEN_LAST_OPERATOR = EXPRTOKEN_OPERATOR_GREATER_EQUAL,
0747:
0748: //
0749: // [35] FunctionName ::= QName - NodeType
0750: //
0751: // followed by symbol handle
0752: //
0753: EXPRTOKEN_FUNCTION_NAME = 32,
0754: //
0755: // [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
0756: // | 'attribute'
0757: // | 'child'
0758: // | 'descendant' | 'descendant-or-self'
0759: // | 'following' | 'following-sibling'
0760: // | 'namespace'
0761: // | 'parent'
0762: // | 'preceding' | 'preceding-sibling'
0763: // | 'self'
0764: //
0765: EXPRTOKEN_AXISNAME_ANCESTOR = 33,
0766: EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF = 34,
0767: EXPRTOKEN_AXISNAME_ATTRIBUTE = 35,
0768: EXPRTOKEN_AXISNAME_CHILD = 36,
0769: EXPRTOKEN_AXISNAME_DESCENDANT = 37,
0770: EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF = 38,
0771: EXPRTOKEN_AXISNAME_FOLLOWING = 39,
0772: EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING = 40,
0773: EXPRTOKEN_AXISNAME_NAMESPACE = 41,
0774: EXPRTOKEN_AXISNAME_PARENT = 42,
0775: EXPRTOKEN_AXISNAME_PRECEDING = 43,
0776: EXPRTOKEN_AXISNAME_PRECEDING_SIBLING = 44,
0777: EXPRTOKEN_AXISNAME_SELF = 45,
0778: //
0779: // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
0780: //
0781: // followed by symbol handle for literal
0782: //
0783: EXPRTOKEN_LITERAL = 46,
0784: //
0785: // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
0786: // [31] Digits ::= [0-9]+
0787: //
0788: // followed by number handle
0789: //
0790: EXPRTOKEN_NUMBER = 47,
0791: //
0792: // [36] VariableReference ::= '$' QName
0793: //
0794: // followed by symbol handle for QName
0795: //
0796: EXPRTOKEN_VARIABLE_REFERENCE = 48;
0797:
0798: private static final String[] fgTokenNames = {
0799: "EXPRTOKEN_OPEN_PAREN", "EXPRTOKEN_CLOSE_PAREN",
0800: "EXPRTOKEN_OPEN_BRACKET", "EXPRTOKEN_CLOSE_BRACKET",
0801: "EXPRTOKEN_PERIOD", "EXPRTOKEN_DOUBLE_PERIOD",
0802: "EXPRTOKEN_ATSIGN", "EXPRTOKEN_COMMA",
0803: "EXPRTOKEN_DOUBLE_COLON", "EXPRTOKEN_NAMETEST_ANY",
0804: "EXPRTOKEN_NAMETEST_NAMESPACE",
0805: "EXPRTOKEN_NAMETEST_QNAME",
0806: "EXPRTOKEN_NODETYPE_COMMENT",
0807: "EXPRTOKEN_NODETYPE_TEXT", "EXPRTOKEN_NODETYPE_PI",
0808: "EXPRTOKEN_NODETYPE_NODE", "EXPRTOKEN_OPERATOR_AND",
0809: "EXPRTOKEN_OPERATOR_OR", "EXPRTOKEN_OPERATOR_MOD",
0810: "EXPRTOKEN_OPERATOR_DIV", "EXPRTOKEN_OPERATOR_MULT",
0811: "EXPRTOKEN_OPERATOR_SLASH",
0812: "EXPRTOKEN_OPERATOR_DOUBLE_SLASH",
0813: "EXPRTOKEN_OPERATOR_UNION", "EXPRTOKEN_OPERATOR_PLUS",
0814: "EXPRTOKEN_OPERATOR_MINUS", "EXPRTOKEN_OPERATOR_EQUAL",
0815: "EXPRTOKEN_OPERATOR_NOT_EQUAL",
0816: "EXPRTOKEN_OPERATOR_LESS",
0817: "EXPRTOKEN_OPERATOR_LESS_EQUAL",
0818: "EXPRTOKEN_OPERATOR_GREATER",
0819: "EXPRTOKEN_OPERATOR_GREATER_EQUAL",
0820: "EXPRTOKEN_FUNCTION_NAME",
0821: "EXPRTOKEN_AXISNAME_ANCESTOR",
0822: "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF",
0823: "EXPRTOKEN_AXISNAME_ATTRIBUTE",
0824: "EXPRTOKEN_AXISNAME_CHILD",
0825: "EXPRTOKEN_AXISNAME_DESCENDANT",
0826: "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF",
0827: "EXPRTOKEN_AXISNAME_FOLLOWING",
0828: "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING",
0829: "EXPRTOKEN_AXISNAME_NAMESPACE",
0830: "EXPRTOKEN_AXISNAME_PARENT",
0831: "EXPRTOKEN_AXISNAME_PRECEDING",
0832: "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING",
0833: "EXPRTOKEN_AXISNAME_SELF", "EXPRTOKEN_LITERAL",
0834: "EXPRTOKEN_NUMBER", "EXPRTOKEN_VARIABLE_REFERENCE" };
0835:
0836: /**
0837: *
0838: */
0839: private static final int INITIAL_TOKEN_COUNT = 1 << 8;
0840: private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
0841: private int fTokenCount = 0; // for writing
0842:
0843: private SymbolTable fSymbolTable;
0844:
0845: // REVISIT: Code something better here. -Ac
0846: private java.util.Hashtable fSymbolMapping = new java.util.Hashtable();
0847:
0848: // REVISIT: Code something better here. -Ac
0849: private java.util.Hashtable fTokenNames = new java.util.Hashtable();
0850:
0851: /**
0852: * Current position in the token list.
0853: */
0854: private int fCurrentTokenIndex;
0855:
0856: //
0857: // Constructors
0858: //
0859:
0860: public Tokens(SymbolTable symbolTable) {
0861: fSymbolTable = symbolTable;
0862: final String[] symbols = { "ancestor", "ancestor-or-self",
0863: "attribute", "child", "descendant",
0864: "descendant-or-self", "following",
0865: "following-sibling", "namespace", "parent",
0866: "preceding", "preceding-sibling", "self", };
0867: for (int i = 0; i < symbols.length; i++) {
0868: fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]),
0869: new Integer(i));
0870: }
0871: fTokenNames.put(new Integer(EXPRTOKEN_OPEN_PAREN),
0872: "EXPRTOKEN_OPEN_PAREN");
0873: fTokenNames.put(new Integer(EXPRTOKEN_CLOSE_PAREN),
0874: "EXPRTOKEN_CLOSE_PAREN");
0875: fTokenNames.put(new Integer(EXPRTOKEN_OPEN_BRACKET),
0876: "EXPRTOKEN_OPEN_BRACKET");
0877: fTokenNames.put(new Integer(EXPRTOKEN_CLOSE_BRACKET),
0878: "EXPRTOKEN_CLOSE_BRACKET");
0879: fTokenNames.put(new Integer(EXPRTOKEN_PERIOD),
0880: "EXPRTOKEN_PERIOD");
0881: fTokenNames.put(new Integer(EXPRTOKEN_DOUBLE_PERIOD),
0882: "EXPRTOKEN_DOUBLE_PERIOD");
0883: fTokenNames.put(new Integer(EXPRTOKEN_ATSIGN),
0884: "EXPRTOKEN_ATSIGN");
0885: fTokenNames.put(new Integer(EXPRTOKEN_COMMA),
0886: "EXPRTOKEN_COMMA");
0887: fTokenNames.put(new Integer(EXPRTOKEN_DOUBLE_COLON),
0888: "EXPRTOKEN_DOUBLE_COLON");
0889: fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_ANY),
0890: "EXPRTOKEN_NAMETEST_ANY");
0891: fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_NAMESPACE),
0892: "EXPRTOKEN_NAMETEST_NAMESPACE");
0893: fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_QNAME),
0894: "EXPRTOKEN_NAMETEST_QNAME");
0895: fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_COMMENT),
0896: "EXPRTOKEN_NODETYPE_COMMENT");
0897: fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_TEXT),
0898: "EXPRTOKEN_NODETYPE_TEXT");
0899: fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_PI),
0900: "EXPRTOKEN_NODETYPE_PI");
0901: fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_NODE),
0902: "EXPRTOKEN_NODETYPE_NODE");
0903: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_AND),
0904: "EXPRTOKEN_OPERATOR_AND");
0905: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_OR),
0906: "EXPRTOKEN_OPERATOR_OR");
0907: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MOD),
0908: "EXPRTOKEN_OPERATOR_MOD");
0909: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_DIV),
0910: "EXPRTOKEN_OPERATOR_DIV");
0911: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MULT),
0912: "EXPRTOKEN_OPERATOR_MULT");
0913: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_SLASH),
0914: "EXPRTOKEN_OPERATOR_SLASH");
0915: fTokenNames.put(
0916: new Integer(EXPRTOKEN_OPERATOR_DOUBLE_SLASH),
0917: "EXPRTOKEN_OPERATOR_DOUBLE_SLASH");
0918: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_UNION),
0919: "EXPRTOKEN_OPERATOR_UNION");
0920: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_PLUS),
0921: "EXPRTOKEN_OPERATOR_PLUS");
0922: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MINUS),
0923: "EXPRTOKEN_OPERATOR_MINUS");
0924: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_EQUAL),
0925: "EXPRTOKEN_OPERATOR_EQUAL");
0926: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_NOT_EQUAL),
0927: "EXPRTOKEN_OPERATOR_NOT_EQUAL");
0928: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_LESS),
0929: "EXPRTOKEN_OPERATOR_LESS");
0930: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_LESS_EQUAL),
0931: "EXPRTOKEN_OPERATOR_LESS_EQUAL");
0932: fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_GREATER),
0933: "EXPRTOKEN_OPERATOR_GREATER");
0934: fTokenNames.put(new Integer(
0935: EXPRTOKEN_OPERATOR_GREATER_EQUAL),
0936: "EXPRTOKEN_OPERATOR_GREATER_EQUAL");
0937: fTokenNames.put(new Integer(EXPRTOKEN_FUNCTION_NAME),
0938: "EXPRTOKEN_FUNCTION_NAME");
0939: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ANCESTOR),
0940: "EXPRTOKEN_AXISNAME_ANCESTOR");
0941: fTokenNames.put(new Integer(
0942: EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF),
0943: "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF");
0944: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ATTRIBUTE),
0945: "EXPRTOKEN_AXISNAME_ATTRIBUTE");
0946: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_CHILD),
0947: "EXPRTOKEN_AXISNAME_CHILD");
0948: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_DESCENDANT),
0949: "EXPRTOKEN_AXISNAME_DESCENDANT");
0950: fTokenNames.put(new Integer(
0951: EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF),
0952: "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF");
0953: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_FOLLOWING),
0954: "EXPRTOKEN_AXISNAME_FOLLOWING");
0955: fTokenNames.put(new Integer(
0956: EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING),
0957: "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING");
0958: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_NAMESPACE),
0959: "EXPRTOKEN_AXISNAME_NAMESPACE");
0960: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PARENT),
0961: "EXPRTOKEN_AXISNAME_PARENT");
0962: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PRECEDING),
0963: "EXPRTOKEN_AXISNAME_PRECEDING");
0964: fTokenNames.put(new Integer(
0965: EXPRTOKEN_AXISNAME_PRECEDING_SIBLING),
0966: "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING");
0967: fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_SELF),
0968: "EXPRTOKEN_AXISNAME_SELF");
0969: fTokenNames.put(new Integer(EXPRTOKEN_LITERAL),
0970: "EXPRTOKEN_LITERAL");
0971: fTokenNames.put(new Integer(EXPRTOKEN_NUMBER),
0972: "EXPRTOKEN_NUMBER");
0973: fTokenNames.put(new Integer(EXPRTOKEN_VARIABLE_REFERENCE),
0974: "EXPRTOKEN_VARIABLE_REFERENCE");
0975: }
0976:
0977: //
0978: // Public methods
0979: //
0980:
0981: // public String getTokenName(int token) {
0982: // if (token < 0 || token >= fgTokenNames.length)
0983: // return null;
0984: // return fgTokenNames[token];
0985: // }
0986: //
0987: public String getTokenString(int token) {
0988: return (String) fTokenNames.get(new Integer(token));
0989: }
0990:
0991: public void addToken(String tokenStr) {
0992: Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
0993: if (tokenInt == null) {
0994: tokenInt = new Integer(fTokenNames.size());
0995: fTokenNames.put(tokenInt, tokenStr);
0996: }
0997: addToken(tokenInt.intValue());
0998: }
0999:
1000: public void addToken(int token) {
1001: try {
1002: fTokens[fTokenCount] = token;
1003: } catch (ArrayIndexOutOfBoundsException ex) {
1004: int[] oldList = fTokens;
1005: fTokens = new int[fTokenCount << 1];
1006: System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
1007: fTokens[fTokenCount] = token;
1008: }
1009: fTokenCount++;
1010: }
1011:
1012: // public int getTokenCount() {
1013: // return fTokenCount;
1014: // }
1015: // public int getToken(int tokenIndex) {
1016: // return fTokens[tokenIndex];
1017: // }
1018:
1019: /**
1020: * Resets the current position to the head of the token list.
1021: */
1022: public void rewind() {
1023: fCurrentTokenIndex = 0;
1024: }
1025:
1026: /**
1027: * Returns true if the {@link #getNextToken()} method
1028: * returns a valid token.
1029: */
1030: public boolean hasMore() {
1031: return fCurrentTokenIndex < fTokenCount;
1032: }
1033:
1034: /**
1035: * Obtains the token at the current position, then advance
1036: * the current position by one.
1037: *
1038: * If there's no such next token, this method throws
1039: * <tt>new XPathException("c-general-xpath");</tt>.
1040: */
1041: public int nextToken() throws XPathException {
1042: if (fCurrentTokenIndex == fTokenCount)
1043: throw new XPathException("c-general-xpath");
1044: return fTokens[fCurrentTokenIndex++];
1045: }
1046:
1047: /**
1048: * Obtains the token at the current position, without advancing
1049: * the current position.
1050: *
1051: * If there's no such next token, this method throws
1052: * <tt>new XPathException("c-general-xpath");</tt>.
1053: */
1054: public int peekToken() throws XPathException {
1055: if (fCurrentTokenIndex == fTokenCount)
1056: throw new XPathException("c-general-xpath");
1057: return fTokens[fCurrentTokenIndex];
1058: }
1059:
1060: /**
1061: * Obtains the token at the current position as a String.
1062: *
1063: * If there's no current token or if the current token
1064: * is not a string token, this method throws
1065: * <tt>new XPathException("c-general-xpath");</tt>.
1066: */
1067: public String nextTokenAsString() throws XPathException {
1068: String s = getTokenString(nextToken());
1069: if (s == null)
1070: throw new XPathException("c-general-xpath");
1071: return s;
1072: }
1073:
1074: public void dumpTokens() {
1075: //if (DUMP_TOKENS) {
1076: for (int i = 0; i < fTokenCount; i++) {
1077: switch (fTokens[i]) {
1078: case EXPRTOKEN_OPEN_PAREN:
1079: System.out.print("<OPEN_PAREN/>");
1080: break;
1081: case EXPRTOKEN_CLOSE_PAREN:
1082: System.out.print("<CLOSE_PAREN/>");
1083: break;
1084: case EXPRTOKEN_OPEN_BRACKET:
1085: System.out.print("<OPEN_BRACKET/>");
1086: break;
1087: case EXPRTOKEN_CLOSE_BRACKET:
1088: System.out.print("<CLOSE_BRACKET/>");
1089: break;
1090: case EXPRTOKEN_PERIOD:
1091: System.out.print("<PERIOD/>");
1092: break;
1093: case EXPRTOKEN_DOUBLE_PERIOD:
1094: System.out.print("<DOUBLE_PERIOD/>");
1095: break;
1096: case EXPRTOKEN_ATSIGN:
1097: System.out.print("<ATSIGN/>");
1098: break;
1099: case EXPRTOKEN_COMMA:
1100: System.out.print("<COMMA/>");
1101: break;
1102: case EXPRTOKEN_DOUBLE_COLON:
1103: System.out.print("<DOUBLE_COLON/>");
1104: break;
1105: case EXPRTOKEN_NAMETEST_ANY:
1106: System.out.print("<NAMETEST_ANY/>");
1107: break;
1108: case EXPRTOKEN_NAMETEST_NAMESPACE:
1109: System.out.print("<NAMETEST_NAMESPACE");
1110: System.out.print(" prefix=\""
1111: + getTokenString(fTokens[++i]) + "\"");
1112: System.out.print("/>");
1113: break;
1114: case EXPRTOKEN_NAMETEST_QNAME:
1115: System.out.print("<NAMETEST_QNAME");
1116: if (fTokens[++i] != -1)
1117: System.out.print(" prefix=\""
1118: + getTokenString(fTokens[i]) + "\"");
1119: System.out.print(" localpart=\""
1120: + getTokenString(fTokens[++i]) + "\"");
1121: System.out.print("/>");
1122: break;
1123: case EXPRTOKEN_NODETYPE_COMMENT:
1124: System.out.print("<NODETYPE_COMMENT/>");
1125: break;
1126: case EXPRTOKEN_NODETYPE_TEXT:
1127: System.out.print("<NODETYPE_TEXT/>");
1128: break;
1129: case EXPRTOKEN_NODETYPE_PI:
1130: System.out.print("<NODETYPE_PI/>");
1131: break;
1132: case EXPRTOKEN_NODETYPE_NODE:
1133: System.out.print("<NODETYPE_NODE/>");
1134: break;
1135: case EXPRTOKEN_OPERATOR_AND:
1136: System.out.print("<OPERATOR_AND/>");
1137: break;
1138: case EXPRTOKEN_OPERATOR_OR:
1139: System.out.print("<OPERATOR_OR/>");
1140: break;
1141: case EXPRTOKEN_OPERATOR_MOD:
1142: System.out.print("<OPERATOR_MOD/>");
1143: break;
1144: case EXPRTOKEN_OPERATOR_DIV:
1145: System.out.print("<OPERATOR_DIV/>");
1146: break;
1147: case EXPRTOKEN_OPERATOR_MULT:
1148: System.out.print("<OPERATOR_MULT/>");
1149: break;
1150: case EXPRTOKEN_OPERATOR_SLASH:
1151: System.out.print("<OPERATOR_SLASH/>");
1152: if (i + 1 < fTokenCount) {
1153: System.out.println();
1154: System.out.print(" ");
1155: }
1156: break;
1157: case EXPRTOKEN_OPERATOR_DOUBLE_SLASH:
1158: System.out.print("<OPERATOR_DOUBLE_SLASH/>");
1159: break;
1160: case EXPRTOKEN_OPERATOR_UNION:
1161: System.out.print("<OPERATOR_UNION/>");
1162: break;
1163: case EXPRTOKEN_OPERATOR_PLUS:
1164: System.out.print("<OPERATOR_PLUS/>");
1165: break;
1166: case EXPRTOKEN_OPERATOR_MINUS:
1167: System.out.print("<OPERATOR_MINUS/>");
1168: break;
1169: case EXPRTOKEN_OPERATOR_EQUAL:
1170: System.out.print("<OPERATOR_EQUAL/>");
1171: break;
1172: case EXPRTOKEN_OPERATOR_NOT_EQUAL:
1173: System.out.print("<OPERATOR_NOT_EQUAL/>");
1174: break;
1175: case EXPRTOKEN_OPERATOR_LESS:
1176: System.out.print("<OPERATOR_LESS/>");
1177: break;
1178: case EXPRTOKEN_OPERATOR_LESS_EQUAL:
1179: System.out.print("<OPERATOR_LESS_EQUAL/>");
1180: break;
1181: case EXPRTOKEN_OPERATOR_GREATER:
1182: System.out.print("<OPERATOR_GREATER/>");
1183: break;
1184: case EXPRTOKEN_OPERATOR_GREATER_EQUAL:
1185: System.out.print("<OPERATOR_GREATER_EQUAL/>");
1186: break;
1187: case EXPRTOKEN_FUNCTION_NAME:
1188: System.out.print("<FUNCTION_NAME");
1189: if (fTokens[++i] != -1)
1190: System.out.print(" prefix=\""
1191: + getTokenString(fTokens[i]) + "\"");
1192: System.out.print(" localpart=\""
1193: + getTokenString(fTokens[++i]) + "\"");
1194: System.out.print("/>");
1195: break;
1196: case EXPRTOKEN_AXISNAME_ANCESTOR:
1197: System.out.print("<AXISNAME_ANCESTOR/>");
1198: break;
1199: case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
1200: System.out.print("<AXISNAME_ANCESTOR_OR_SELF/>");
1201: break;
1202: case EXPRTOKEN_AXISNAME_ATTRIBUTE:
1203: System.out.print("<AXISNAME_ATTRIBUTE/>");
1204: break;
1205: case EXPRTOKEN_AXISNAME_CHILD:
1206: System.out.print("<AXISNAME_CHILD/>");
1207: break;
1208: case EXPRTOKEN_AXISNAME_DESCENDANT:
1209: System.out.print("<AXISNAME_DESCENDANT/>");
1210: break;
1211: case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
1212: System.out.print("<AXISNAME_DESCENDANT_OR_SELF/>");
1213: break;
1214: case EXPRTOKEN_AXISNAME_FOLLOWING:
1215: System.out.print("<AXISNAME_FOLLOWING/>");
1216: break;
1217: case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
1218: System.out.print("<AXISNAME_FOLLOWING_SIBLING/>");
1219: break;
1220: case EXPRTOKEN_AXISNAME_NAMESPACE:
1221: System.out.print("<AXISNAME_NAMESPACE/>");
1222: break;
1223: case EXPRTOKEN_AXISNAME_PARENT:
1224: System.out.print("<AXISNAME_PARENT/>");
1225: break;
1226: case EXPRTOKEN_AXISNAME_PRECEDING:
1227: System.out.print("<AXISNAME_PRECEDING/>");
1228: break;
1229: case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
1230: System.out.print("<AXISNAME_PRECEDING_SIBLING/>");
1231: break;
1232: case EXPRTOKEN_AXISNAME_SELF:
1233: System.out.print("<AXISNAME_SELF/>");
1234: break;
1235: case EXPRTOKEN_LITERAL:
1236: System.out.print("<LITERAL");
1237: System.out.print(" value=\""
1238: + getTokenString(fTokens[++i]) + "\"");
1239: System.out.print("/>");
1240: break;
1241: case EXPRTOKEN_NUMBER:
1242: System.out.print("<NUMBER");
1243: System.out.print(" whole=\""
1244: + getTokenString(fTokens[++i]) + "\"");
1245: System.out.print(" part=\""
1246: + getTokenString(fTokens[++i]) + "\"");
1247: System.out.print("/>");
1248: break;
1249: case EXPRTOKEN_VARIABLE_REFERENCE:
1250: System.out.print("<VARIABLE_REFERENCE");
1251: if (fTokens[++i] != -1)
1252: System.out.print(" prefix=\""
1253: + getTokenString(fTokens[i]) + "\"");
1254: System.out.print(" localpart=\""
1255: + getTokenString(fTokens[++i]) + "\"");
1256: System.out.print("/>");
1257: break;
1258: default:
1259: System.out.println("<???/>");
1260: }
1261: }
1262: System.out.println();
1263: //}
1264: }
1265:
1266: } // class Tokens
1267:
1268: /**
1269: * @xerces.internal
1270: *
1271: * @author Glenn Marcy, IBM
1272: * @author Andy Clark, IBM
1273: *
1274: * @version $Id: XPath.java 572107 2007-09-02 18:40:40Z mrglavas $
1275: */
1276: private static class Scanner {
1277:
1278: /**
1279: * 7-bit ASCII subset
1280: *
1281: * 0 1 2 3 4 5 6 7 8 9 A B C D E F
1282: * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
1283: * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
1284: * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
1285: * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
1286: * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
1287: * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
1288: * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
1289: * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
1290: */
1291: private static final byte CHARTYPE_INVALID = 0, // invalid XML character
1292: CHARTYPE_OTHER = 1, // not special - one of "#%&;?\^`{}~" or DEL
1293: CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
1294: CHARTYPE_EXCLAMATION = 3, // '!' (0x21)
1295: CHARTYPE_QUOTE = 4, // '\"' or '\'' (0x22 and 0x27)
1296: CHARTYPE_DOLLAR = 5, // '$' (0x24)
1297: CHARTYPE_OPEN_PAREN = 6, // '(' (0x28)
1298: CHARTYPE_CLOSE_PAREN = 7, // ')' (0x29)
1299: CHARTYPE_STAR = 8, // '*' (0x2A)
1300: CHARTYPE_PLUS = 9, // '+' (0x2B)
1301: CHARTYPE_COMMA = 10, // ',' (0x2C)
1302: CHARTYPE_MINUS = 11, // '-' (0x2D)
1303: CHARTYPE_PERIOD = 12, // '.' (0x2E)
1304: CHARTYPE_SLASH = 13, // '/' (0x2F)
1305: CHARTYPE_DIGIT = 14, // '0'-'9' (0x30 to 0x39)
1306: CHARTYPE_COLON = 15, // ':' (0x3A)
1307: CHARTYPE_LESS = 16, // '<' (0x3C)
1308: CHARTYPE_EQUAL = 17, // '=' (0x3D)
1309: CHARTYPE_GREATER = 18, // '>' (0x3E)
1310: CHARTYPE_ATSIGN = 19, // '@' (0x40)
1311: CHARTYPE_LETTER = 20, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
1312: CHARTYPE_OPEN_BRACKET = 21, // '[' (0x5B)
1313: CHARTYPE_CLOSE_BRACKET = 22, // ']' (0x5D)
1314: CHARTYPE_UNDERSCORE = 23, // '_' (0x5F)
1315: CHARTYPE_UNION = 24, // '|' (0x7C)
1316: CHARTYPE_NONASCII = 25; // Non-ASCII Unicode codepoint (>= 0x80)
1317:
1318: private static final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0,
1319: 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1320: 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 1, 5, 1, 1, 4, 6, 7,
1321: 8, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14, 14, 14, 14,
1322: 14, 14, 15, 1, 16, 17, 18, 1, 19, 20, 20, 20, 20, 20,
1323: 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
1324: 20, 20, 20, 20, 20, 20, 20, 21, 1, 22, 1, 23, 1, 20,
1325: 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
1326: 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1, 24, 1,
1327: 1, 1 };
1328:
1329: /**
1330: * Symbol literals
1331: */
1332:
1333: //
1334: // Data
1335: //
1336: /** Symbol table. */
1337: private SymbolTable fSymbolTable;
1338:
1339: // symbols
1340:
1341: private static final String fAndSymbol = "and".intern();
1342: private static final String fOrSymbol = "or".intern();
1343: private static final String fModSymbol = "mod".intern();
1344: private static final String fDivSymbol = "div".intern();
1345:
1346: private static final String fCommentSymbol = "comment".intern();
1347: private static final String fTextSymbol = "text".intern();
1348: private static final String fPISymbol = "processing-instruction"
1349: .intern();
1350: private static final String fNodeSymbol = "node".intern();
1351:
1352: private static final String fAncestorSymbol = "ancestor"
1353: .intern();
1354: private static final String fAncestorOrSelfSymbol = "ancestor-or-self"
1355: .intern();
1356: private static final String fAttributeSymbol = "attribute"
1357: .intern();
1358: private static final String fChildSymbol = "child".intern();
1359: private static final String fDescendantSymbol = "descendant"
1360: .intern();
1361: private static final String fDescendantOrSelfSymbol = "descendant-or-self"
1362: .intern();
1363: private static final String fFollowingSymbol = "following"
1364: .intern();
1365: private static final String fFollowingSiblingSymbol = "following-sibling"
1366: .intern();
1367: private static final String fNamespaceSymbol = "namespace"
1368: .intern();
1369: private static final String fParentSymbol = "parent".intern();
1370: private static final String fPrecedingSymbol = "preceding"
1371: .intern();
1372: private static final String fPrecedingSiblingSymbol = "preceding-sibling"
1373: .intern();
1374: private static final String fSelfSymbol = "self".intern();
1375:
1376: //
1377: // Constructors
1378: //
1379:
1380: /** Constructs an XPath expression scanner. */
1381: public Scanner(SymbolTable symbolTable) {
1382:
1383: // save pool and tokens
1384: fSymbolTable = symbolTable;
1385:
1386: } // <init>(SymbolTable)
1387:
1388: /**
1389: *
1390: */
1391: public boolean scanExpr(SymbolTable symbolTable,
1392: XPath.Tokens tokens, String data, int currentOffset,
1393: int endOffset) throws XPathException {
1394:
1395: int nameOffset;
1396: String nameHandle, prefixHandle;
1397: boolean starIsMultiplyOperator = false;
1398: int ch;
1399:
1400: while (true) {
1401: if (currentOffset == endOffset) {
1402: break;
1403: }
1404: ch = data.charAt(currentOffset);
1405: //
1406: // [39] ExprWhitespace ::= S
1407: //
1408: while (ch == ' ' || ch == 0x0A || ch == 0x09
1409: || ch == 0x0D) {
1410: if (++currentOffset == endOffset) {
1411: break;
1412: }
1413: ch = data.charAt(currentOffset);
1414: }
1415: if (currentOffset == endOffset) {
1416: break;
1417: }
1418: //
1419: // [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
1420: // | NameTest | NodeType | Operator | FunctionName
1421: // | AxisName | Literal | Number | VariableReference
1422: //
1423: byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
1424: : fASCIICharMap[ch];
1425: switch (chartype) {
1426: case CHARTYPE_OPEN_PAREN: // '('
1427: addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1428: starIsMultiplyOperator = false;
1429: if (++currentOffset == endOffset) {
1430: break;
1431: }
1432: break;
1433: case CHARTYPE_CLOSE_PAREN: // ')'
1434: addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN);
1435: starIsMultiplyOperator = true;
1436: if (++currentOffset == endOffset) {
1437: break;
1438: }
1439: break;
1440: case CHARTYPE_OPEN_BRACKET: // '['
1441: addToken(tokens,
1442: XPath.Tokens.EXPRTOKEN_OPEN_BRACKET);
1443: starIsMultiplyOperator = false;
1444: if (++currentOffset == endOffset) {
1445: break;
1446: }
1447: break;
1448: case CHARTYPE_CLOSE_BRACKET: // ']'
1449: addToken(tokens,
1450: XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET);
1451: starIsMultiplyOperator = true;
1452: if (++currentOffset == endOffset) {
1453: break;
1454: }
1455: break;
1456: //
1457: // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1458: // ^^^^^^^^^^
1459: //
1460: case CHARTYPE_PERIOD: // '.', '..' or '.' Digits
1461: if (currentOffset + 1 == endOffset) {
1462: addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1463: starIsMultiplyOperator = true;
1464: currentOffset++;
1465: break;
1466: }
1467: ch = data.charAt(currentOffset + 1);
1468: if (ch == '.') { // '..'
1469: addToken(tokens,
1470: XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD);
1471: starIsMultiplyOperator = true;
1472: currentOffset += 2;
1473: } else if (ch >= '0' && ch <= '9') {
1474: addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1475: starIsMultiplyOperator = true;
1476: currentOffset = scanNumber(tokens, data,
1477: endOffset, currentOffset/*, encoding*/);
1478: } else if (ch == '/') {
1479: addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1480: starIsMultiplyOperator = true;
1481: currentOffset++;
1482: } else if (ch == '|') {
1483: addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
1484: starIsMultiplyOperator = true;
1485: currentOffset++;
1486: break;
1487: } else if (ch == ' ' || ch == 0x0A || ch == 0x09
1488: || ch == 0x0D) {
1489: // this is legal if the next token is non-existent or |
1490: do {
1491: if (++currentOffset == endOffset) {
1492: break;
1493: }
1494: ch = data.charAt(currentOffset);
1495: } while (ch == ' ' || ch == 0x0A || ch == 0x09
1496: || ch == 0x0D);
1497: if (currentOffset == endOffset || ch == '|') {
1498: addToken(tokens,
1499: XPath.Tokens.EXPRTOKEN_PERIOD);
1500: starIsMultiplyOperator = true;
1501: break;
1502: }
1503: throw new XPathException("c-general-xpath");
1504: } else { // '.'
1505: throw new XPathException("c-general-xpath");
1506: }
1507: if (currentOffset == endOffset) {
1508: break;
1509: }
1510: break;
1511: case CHARTYPE_ATSIGN: // '@'
1512: addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN);
1513: starIsMultiplyOperator = false;
1514: if (++currentOffset == endOffset) {
1515: break;
1516: }
1517: break;
1518: case CHARTYPE_COMMA: // ','
1519: addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA);
1520: starIsMultiplyOperator = false;
1521: if (++currentOffset == endOffset) {
1522: break;
1523: }
1524: break;
1525: case CHARTYPE_COLON: // '::'
1526: if (++currentOffset == endOffset) {
1527: // System.out.println("abort 1a");
1528: return false; // REVISIT
1529: }
1530: ch = data.charAt(currentOffset);
1531: if (ch != ':') {
1532: // System.out.println("abort 1b");
1533: return false; // REVISIT
1534: }
1535: addToken(tokens,
1536: XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
1537: starIsMultiplyOperator = false;
1538: if (++currentOffset == endOffset) {
1539: break;
1540: }
1541: break;
1542: case CHARTYPE_SLASH: // '/' and '//'
1543: if (++currentOffset == endOffset) {
1544: addToken(tokens,
1545: XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1546: starIsMultiplyOperator = false;
1547: break;
1548: }
1549: ch = data.charAt(currentOffset);
1550: if (ch == '/') { // '//'
1551: addToken(
1552: tokens,
1553: XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH);
1554: starIsMultiplyOperator = false;
1555: if (++currentOffset == endOffset) {
1556: break;
1557: }
1558: } else {
1559: addToken(tokens,
1560: XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
1561: starIsMultiplyOperator = false;
1562: }
1563: break;
1564: case CHARTYPE_UNION: // '|'
1565: addToken(tokens,
1566: XPath.Tokens.EXPRTOKEN_OPERATOR_UNION);
1567: starIsMultiplyOperator = false;
1568: if (++currentOffset == endOffset) {
1569: break;
1570: }
1571: break;
1572: case CHARTYPE_PLUS: // '+'
1573: addToken(tokens,
1574: XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS);
1575: starIsMultiplyOperator = false;
1576: if (++currentOffset == endOffset) {
1577: break;
1578: }
1579: break;
1580: case CHARTYPE_MINUS: // '-'
1581: addToken(tokens,
1582: XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS);
1583: starIsMultiplyOperator = false;
1584: if (++currentOffset == endOffset) {
1585: break;
1586: }
1587: break;
1588: case CHARTYPE_EQUAL: // '='
1589: addToken(tokens,
1590: XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL);
1591: starIsMultiplyOperator = false;
1592: if (++currentOffset == endOffset) {
1593: break;
1594: }
1595: break;
1596: case CHARTYPE_EXCLAMATION: // '!='
1597: if (++currentOffset == endOffset) {
1598: // System.out.println("abort 2a");
1599: return false; // REVISIT
1600: }
1601: ch = data.charAt(currentOffset);
1602: if (ch != '=') {
1603: // System.out.println("abort 2b");
1604: return false; // REVISIT
1605: }
1606: addToken(tokens,
1607: XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL);
1608: starIsMultiplyOperator = false;
1609: if (++currentOffset == endOffset) {
1610: break;
1611: }
1612: break;
1613: case CHARTYPE_LESS: // '<' and '<='
1614: if (++currentOffset == endOffset) {
1615: addToken(tokens,
1616: XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1617: starIsMultiplyOperator = false;
1618: break;
1619: }
1620: ch = data.charAt(currentOffset);
1621: if (ch == '=') { // '<='
1622: addToken(
1623: tokens,
1624: XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL);
1625: starIsMultiplyOperator = false;
1626: if (++currentOffset == endOffset) {
1627: break;
1628: }
1629: } else {
1630: addToken(tokens,
1631: XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
1632: starIsMultiplyOperator = false;
1633: }
1634: break;
1635: case CHARTYPE_GREATER: // '>' and '>='
1636: if (++currentOffset == endOffset) {
1637: addToken(tokens,
1638: XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1639: starIsMultiplyOperator = false;
1640: break;
1641: }
1642: ch = data.charAt(currentOffset);
1643: if (ch == '=') { // '>='
1644: addToken(
1645: tokens,
1646: XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL);
1647: starIsMultiplyOperator = false;
1648: if (++currentOffset == endOffset) {
1649: break;
1650: }
1651: } else {
1652: addToken(tokens,
1653: XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
1654: starIsMultiplyOperator = false;
1655: }
1656: break;
1657: //
1658: // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
1659: //
1660: case CHARTYPE_QUOTE: // '\"' or '\''
1661: int qchar = ch;
1662: if (++currentOffset == endOffset) {
1663: // System.out.println("abort 2c");
1664: return false; // REVISIT
1665: }
1666: ch = data.charAt(currentOffset);
1667: int litOffset = currentOffset;
1668: while (ch != qchar) {
1669: if (++currentOffset == endOffset) {
1670: // System.out.println("abort 2d");
1671: return false; // REVISIT
1672: }
1673: ch = data.charAt(currentOffset);
1674: }
1675: int litLength = currentOffset - litOffset;
1676: addToken(tokens, XPath.Tokens.EXPRTOKEN_LITERAL);
1677: starIsMultiplyOperator = true;
1678: tokens.addToken(symbolTable
1679: .addSymbol(data.substring(litOffset,
1680: litOffset + litLength)));
1681: if (++currentOffset == endOffset) {
1682: break;
1683: }
1684: break;
1685: //
1686: // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
1687: // [31] Digits ::= [0-9]+
1688: //
1689: case CHARTYPE_DIGIT:
1690: addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
1691: starIsMultiplyOperator = true;
1692: currentOffset = scanNumber(tokens, data, endOffset,
1693: currentOffset/*, encoding*/);
1694: break;
1695: //
1696: // [36] VariableReference ::= '$' QName
1697: //
1698: case CHARTYPE_DOLLAR:
1699: if (++currentOffset == endOffset) {
1700: // System.out.println("abort 3a");
1701: return false; // REVISIT
1702: }
1703: nameOffset = currentOffset;
1704: currentOffset = scanNCName(data, endOffset,
1705: currentOffset);
1706: if (currentOffset == nameOffset) {
1707: // System.out.println("abort 3b");
1708: return false; // REVISIT
1709: }
1710: if (currentOffset < endOffset) {
1711: ch = data.charAt(currentOffset);
1712: } else {
1713: ch = -1;
1714: }
1715: nameHandle = symbolTable.addSymbol(data.substring(
1716: nameOffset, currentOffset));
1717: if (ch != ':') {
1718: prefixHandle = XMLSymbols.EMPTY_STRING;
1719: } else {
1720: prefixHandle = nameHandle;
1721: if (++currentOffset == endOffset) {
1722: // System.out.println("abort 4a");
1723: return false; // REVISIT
1724: }
1725: nameOffset = currentOffset;
1726: currentOffset = scanNCName(data, endOffset,
1727: currentOffset);
1728: if (currentOffset == nameOffset) {
1729: // System.out.println("abort 4b");
1730: return false; // REVISIT
1731: }
1732: if (currentOffset < endOffset) {
1733: ch = data.charAt(currentOffset);
1734: } else {
1735: ch = -1;
1736: }
1737: nameHandle = symbolTable.addSymbol(data
1738: .substring(nameOffset, currentOffset));
1739: }
1740: addToken(tokens,
1741: XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE);
1742: starIsMultiplyOperator = true;
1743: tokens.addToken(prefixHandle);
1744: tokens.addToken(nameHandle);
1745: break;
1746: //
1747: // [37] NameTest ::= '*' | NCName ':' '*' | QName
1748: // [34] MultiplyOperator ::= '*'
1749: //
1750: case CHARTYPE_STAR: // '*'
1751: //
1752: // 3.7 Lexical Structure
1753: //
1754: // If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1755: // an Operator, then a * must be recognized as a MultiplyOperator.
1756: //
1757: // Otherwise, the token must not be recognized as a MultiplyOperator.
1758: //
1759: if (starIsMultiplyOperator) {
1760: addToken(tokens,
1761: XPath.Tokens.EXPRTOKEN_OPERATOR_MULT);
1762: starIsMultiplyOperator = false;
1763: } else {
1764: addToken(tokens,
1765: XPath.Tokens.EXPRTOKEN_NAMETEST_ANY);
1766: starIsMultiplyOperator = true;
1767: }
1768: if (++currentOffset == endOffset) {
1769: break;
1770: }
1771: break;
1772: //
1773: // NCName, QName and non-terminals
1774: //
1775: case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic)
1776: case CHARTYPE_LETTER:
1777: case CHARTYPE_UNDERSCORE:
1778: //
1779: // 3.7 Lexical Structure
1780: //
1781: // If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1782: // an Operator, then an NCName must be recognized as an OperatorName.
1783: //
1784: // If the character following an NCName (possibly after intervening ExprWhitespace) is (,
1785: // then the token must be recognized as a NodeType or a FunctionName.
1786: //
1787: // If the two characters following an NCName (possibly after intervening ExprWhitespace)
1788: // are ::, then the token must be recognized as an AxisName.
1789: //
1790: // Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
1791: // FunctionName, or an AxisName.
1792: //
1793: // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
1794: // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
1795: // [35] FunctionName ::= QName - NodeType
1796: // [6] AxisName ::= (see above)
1797: //
1798: // [37] NameTest ::= '*' | NCName ':' '*' | QName
1799: // [5] NCName ::= (Letter | '_') (NCNameChar)*
1800: // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar')
1801: // [?] QName ::= (NCName ':')? NCName
1802: // [?] Letter ::= [A-Za-z] (ascii subset of 'Letter')
1803: // [?] Digit ::= [0-9] (ascii subset of 'Digit')
1804: //
1805: nameOffset = currentOffset;
1806: currentOffset = scanNCName(data, endOffset,
1807: currentOffset);
1808: if (currentOffset == nameOffset) {
1809: // System.out.println("abort 4c");
1810: return false; // REVISIT
1811: }
1812: if (currentOffset < endOffset) {
1813: ch = data.charAt(currentOffset);
1814: } else {
1815: ch = -1;
1816: }
1817: nameHandle = symbolTable.addSymbol(data.substring(
1818: nameOffset, currentOffset));
1819: boolean isNameTestNCName = false;
1820: boolean isAxisName = false;
1821: prefixHandle = XMLSymbols.EMPTY_STRING;
1822: if (ch == ':') {
1823: if (++currentOffset == endOffset) {
1824: // System.out.println("abort 5");
1825: return false; // REVISIT
1826: }
1827: ch = data.charAt(currentOffset);
1828: if (ch == '*') {
1829: if (++currentOffset < endOffset) {
1830: ch = data.charAt(currentOffset);
1831: }
1832: isNameTestNCName = true;
1833: } else if (ch == ':') {
1834: if (++currentOffset < endOffset) {
1835: ch = data.charAt(currentOffset);
1836: }
1837: isAxisName = true;
1838: } else {
1839: prefixHandle = nameHandle;
1840: nameOffset = currentOffset;
1841: currentOffset = scanNCName(data, endOffset,
1842: currentOffset);
1843: if (currentOffset == nameOffset) {
1844: // System.out.println("abort 5b");
1845: return false; // REVISIT
1846: }
1847: if (currentOffset < endOffset) {
1848: ch = data.charAt(currentOffset);
1849: } else {
1850: ch = -1;
1851: }
1852: nameHandle = symbolTable.addSymbol(data
1853: .substring(nameOffset,
1854: currentOffset));
1855: }
1856: }
1857: //
1858: // [39] ExprWhitespace ::= S
1859: //
1860: while (ch == ' ' || ch == 0x0A || ch == 0x09
1861: || ch == 0x0D) {
1862: if (++currentOffset == endOffset) {
1863: break;
1864: }
1865: ch = data.charAt(currentOffset);
1866: }
1867: //
1868: // If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
1869: // an Operator, then an NCName must be recognized as an OperatorName.
1870: //
1871: if (starIsMultiplyOperator) {
1872: if (nameHandle == fAndSymbol) {
1873: addToken(tokens,
1874: XPath.Tokens.EXPRTOKEN_OPERATOR_AND);
1875: starIsMultiplyOperator = false;
1876: } else if (nameHandle == fOrSymbol) {
1877: addToken(tokens,
1878: XPath.Tokens.EXPRTOKEN_OPERATOR_OR);
1879: starIsMultiplyOperator = false;
1880: } else if (nameHandle == fModSymbol) {
1881: addToken(tokens,
1882: XPath.Tokens.EXPRTOKEN_OPERATOR_MOD);
1883: starIsMultiplyOperator = false;
1884: } else if (nameHandle == fDivSymbol) {
1885: addToken(tokens,
1886: XPath.Tokens.EXPRTOKEN_OPERATOR_DIV);
1887: starIsMultiplyOperator = false;
1888: } else {
1889: // System.out.println("abort 6");
1890: return false; // REVISIT
1891: }
1892: if (isNameTestNCName) {
1893: // System.out.println("abort 7");
1894: return false; // REVISIT - NCName:* where an OperatorName is required
1895: } else if (isAxisName) {
1896: // System.out.println("abort 8");
1897: return false; // REVISIT - AxisName:: where an OperatorName is required
1898: }
1899: break;
1900: }
1901: //
1902: // If the character following an NCName (possibly after intervening ExprWhitespace) is (,
1903: // then the token must be recognized as a NodeType or a FunctionName.
1904: //
1905: if (ch == '(' && !isNameTestNCName && !isAxisName) {
1906: if (nameHandle == fCommentSymbol) {
1907: addToken(
1908: tokens,
1909: XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT);
1910: } else if (nameHandle == fTextSymbol) {
1911: addToken(
1912: tokens,
1913: XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT);
1914: } else if (nameHandle == fPISymbol) {
1915: addToken(tokens,
1916: XPath.Tokens.EXPRTOKEN_NODETYPE_PI);
1917: } else if (nameHandle == fNodeSymbol) {
1918: addToken(
1919: tokens,
1920: XPath.Tokens.EXPRTOKEN_NODETYPE_NODE);
1921: } else {
1922: addToken(
1923: tokens,
1924: XPath.Tokens.EXPRTOKEN_FUNCTION_NAME);
1925: tokens.addToken(prefixHandle);
1926: tokens.addToken(nameHandle);
1927: }
1928: addToken(tokens,
1929: XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
1930: starIsMultiplyOperator = false;
1931: if (++currentOffset == endOffset) {
1932: break;
1933: }
1934: break;
1935: }
1936: //
1937: // If the two characters following an NCName (possibly after intervening ExprWhitespace)
1938: // are ::, then the token must be recognized as an AxisName.
1939: //
1940: if (isAxisName
1941: || (ch == ':'
1942: && currentOffset + 1 < endOffset && data
1943: .charAt(currentOffset + 1) == ':')) {
1944: if (nameHandle == fAncestorSymbol) {
1945: addToken(
1946: tokens,
1947: XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR);
1948: } else if (nameHandle == fAncestorOrSelfSymbol) {
1949: addToken(
1950: tokens,
1951: XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF);
1952: } else if (nameHandle == fAttributeSymbol) {
1953: addToken(
1954: tokens,
1955: XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE);
1956: } else if (nameHandle == fChildSymbol) {
1957: addToken(
1958: tokens,
1959: XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD);
1960: } else if (nameHandle == fDescendantSymbol) {
1961: addToken(
1962: tokens,
1963: XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT);
1964: } else if (nameHandle == fDescendantOrSelfSymbol) {
1965: addToken(
1966: tokens,
1967: XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF);
1968: } else if (nameHandle == fFollowingSymbol) {
1969: addToken(
1970: tokens,
1971: XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING);
1972: } else if (nameHandle == fFollowingSiblingSymbol) {
1973: addToken(
1974: tokens,
1975: XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING);
1976: } else if (nameHandle == fNamespaceSymbol) {
1977: addToken(
1978: tokens,
1979: XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE);
1980: } else if (nameHandle == fParentSymbol) {
1981: addToken(
1982: tokens,
1983: XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT);
1984: } else if (nameHandle == fPrecedingSymbol) {
1985: addToken(
1986: tokens,
1987: XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING);
1988: } else if (nameHandle == fPrecedingSiblingSymbol) {
1989: addToken(
1990: tokens,
1991: XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING);
1992: } else if (nameHandle == fSelfSymbol) {
1993: addToken(
1994: tokens,
1995: XPath.Tokens.EXPRTOKEN_AXISNAME_SELF);
1996: } else {
1997: // System.out.println("abort 9");
1998: return false; // REVISIT
1999: }
2000: if (isNameTestNCName) {
2001: // System.out.println("abort 10");
2002: return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required
2003: }
2004: addToken(tokens,
2005: XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
2006: starIsMultiplyOperator = false;
2007: if (!isAxisName) {
2008: currentOffset++;
2009: if (++currentOffset == endOffset) {
2010: break;
2011: }
2012: }
2013: break;
2014: }
2015: //
2016: // Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
2017: // FunctionName, or an AxisName.
2018: //
2019: if (isNameTestNCName) {
2020: addToken(
2021: tokens,
2022: XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE);
2023: starIsMultiplyOperator = true;
2024: tokens.addToken(nameHandle);
2025: } else {
2026: addToken(tokens,
2027: XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME);
2028: starIsMultiplyOperator = true;
2029: tokens.addToken(prefixHandle);
2030: tokens.addToken(nameHandle);
2031: }
2032: break;
2033: }
2034: }
2035: if (XPath.Tokens.DUMP_TOKENS) {
2036: tokens.dumpTokens();
2037: }
2038: return true;
2039: }
2040:
2041: //
2042: // [5] NCName ::= (Letter | '_') (NCNameChar)*
2043: // [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
2044: //
2045: int scanNCName(String data, int endOffset, int currentOffset) {
2046: int ch = data.charAt(currentOffset);
2047: if (ch >= 0x80) {
2048: if (!XMLChar.isNameStart(ch))
2049: /*** // REVISIT: Make sure this is a negation. ***
2050: if ((XMLCharacterProperties.fgCharFlags[ch] &
2051: XMLCharacterProperties.E_InitialNameCharFlag) == 0)
2052: /***/
2053: {
2054: return currentOffset;
2055: }
2056: } else {
2057: byte chartype = fASCIICharMap[ch];
2058: if (chartype != CHARTYPE_LETTER
2059: && chartype != CHARTYPE_UNDERSCORE) {
2060: return currentOffset;
2061: }
2062: }
2063: while (++currentOffset < endOffset) {
2064: ch = data.charAt(currentOffset);
2065: if (ch >= 0x80) {
2066: if (!XMLChar.isName(ch))
2067: /*** // REVISIT: Make sure this is a negation. ***
2068: if ((XMLCharacterProperties.fgCharFlags[ch] &
2069: XMLCharacterProperties.E_NameCharFlag) == 0)
2070: /***/
2071: {
2072: break;
2073: }
2074: } else {
2075: byte chartype = fASCIICharMap[ch];
2076: if (chartype != CHARTYPE_LETTER
2077: && chartype != CHARTYPE_DIGIT
2078: && chartype != CHARTYPE_PERIOD
2079: && chartype != CHARTYPE_MINUS
2080: && chartype != CHARTYPE_UNDERSCORE) {
2081: break;
2082: }
2083: }
2084: }
2085: return currentOffset;
2086: }
2087:
2088: //
2089: // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
2090: // [31] Digits ::= [0-9]+
2091: //
2092: private int scanNumber(XPath.Tokens tokens,
2093: String/*byte[]*/data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) {
2094: int ch = data.charAt(currentOffset);
2095: int whole = 0;
2096: int part = 0;
2097: while (ch >= '0' && ch <= '9') {
2098: whole = (whole * 10) + (ch - '0');
2099: if (++currentOffset == endOffset) {
2100: break;
2101: }
2102: ch = data.charAt(currentOffset);
2103: }
2104: if (ch == '.') {
2105: if (++currentOffset < endOffset) {
2106: /** int start = currentOffset; **/
2107: ch = data.charAt(currentOffset);
2108: while (ch >= '0' && ch <= '9') {
2109: part = (part * 10) + (ch - '0');
2110: if (++currentOffset == endOffset) {
2111: break;
2112: }
2113: ch = data.charAt(currentOffset);
2114: }
2115: if (part != 0) {
2116: /***
2117: part = tokens.addSymbol(data, start, currentOffset - start, encoding);
2118: /***/
2119: throw new RuntimeException("find a solution!");
2120: //part = fStringPool.addSymbol(data.substring(start, currentOffset));
2121: /***/
2122: }
2123: }
2124: }
2125: tokens.addToken(whole);
2126: tokens.addToken(part);
2127: return currentOffset;
2128: }
2129:
2130: //
2131: // Protected methods
2132: //
2133:
2134: /**
2135: * This method adds the specified token to the token list. By
2136: * default, this method allows all tokens. However, subclasses
2137: * of the XPathExprScanner can override this method in order
2138: * to disallow certain tokens from being used in the scanned
2139: * XPath expression. This is a convenient way of allowing only
2140: * a subset of XPath.
2141: */
2142: protected void addToken(XPath.Tokens tokens, int token)
2143: throws XPathException {
2144: tokens.addToken(token);
2145: } // addToken(int)
2146:
2147: } // class Scanner
2148:
2149: //
2150: // MAIN
2151: //
2152:
2153: /** Main program entry. */
2154: public static void main(String[] argv) throws Exception {
2155:
2156: for (int i = 0; i < argv.length; i++) {
2157: final String expression = argv[i];
2158: System.out.println("# XPath expression: \"" + expression
2159: + '"');
2160: try {
2161: SymbolTable symbolTable = new SymbolTable();
2162: XPath xpath = new XPath(expression, symbolTable, null);
2163: System.out.println("expanded xpath: \""
2164: + xpath.toString() + '"');
2165: } catch (XPathException e) {
2166: System.out.println("error: " + e.getMessage());
2167: }
2168: }
2169:
2170: } // main(String[])
2171:
2172: } // class XPath
|