0001: package net.sf.saxon.expr;
0002:
0003: import net.sf.saxon.Err;
0004: import net.sf.saxon.event.LocationProvider;
0005: import net.sf.saxon.instruct.Block;
0006: import net.sf.saxon.instruct.Executable;
0007: import net.sf.saxon.instruct.LocationMap;
0008: import net.sf.saxon.instruct.TraceExpression;
0009: import net.sf.saxon.om.Axis;
0010: import net.sf.saxon.om.NameChecker;
0011: import net.sf.saxon.om.NamespaceConstant;
0012: import net.sf.saxon.om.QNameException;
0013: import net.sf.saxon.pattern.*;
0014: import net.sf.saxon.sort.Reverser;
0015: import net.sf.saxon.style.StandardNames;
0016: import net.sf.saxon.trace.Location;
0017: import net.sf.saxon.trans.DynamicError;
0018: import net.sf.saxon.trans.StaticError;
0019: import net.sf.saxon.trans.XPathException;
0020: import net.sf.saxon.type.*;
0021: import net.sf.saxon.value.*;
0022:
0023: import java.io.Serializable;
0024: import java.util.ArrayList;
0025: import java.util.List;
0026: import java.util.Stack;
0027:
0028: /**
0029: * Parser for XPath expressions and XSLT patterns.
0030: *
0031: * This code was originally inspired by James Clark's xt but has been totally rewritten (several times)
0032: *
0033: * @author Michael Kay
0034: */
0035:
0036: public class ExpressionParser {
0037:
0038: protected Tokenizer t;
0039: protected StaticContext env;
0040: protected Stack rangeVariables = null;
0041: // The stack holds a list of range variables that are in scope.
0042: // Each entry on the stack is a VariableDeclaration object containing details
0043: // of the variable.
0044:
0045: protected NameChecker nameChecker;
0046:
0047: protected boolean scanOnly = false;
0048: // scanOnly is set to true while attributes in direct element constructors
0049: // are being processed. We need to parse enclosed expressions in the attribute
0050: // in order to find the end of the attribute value, but we don't yet know the
0051: // full namespace context at this stage.
0052:
0053: protected int language = XPATH; // know which language we are parsing, for diagnostics
0054: protected static final int XPATH = 0;
0055: protected static final int XSLT_PATTERN = 1;
0056: protected static final int SEQUENCE_TYPE = 2;
0057: protected static final int XQUERY = 3;
0058:
0059: public ExpressionParser() {
0060: }
0061:
0062: public Tokenizer getTokenizer() {
0063: return t;
0064: }
0065:
0066: /**
0067: * Read the next token, catching any exception thrown by the tokenizer
0068: */
0069:
0070: protected void nextToken() throws StaticError {
0071: try {
0072: t.next();
0073: } catch (StaticError err) {
0074: grumble(err.getMessage());
0075: }
0076: }
0077:
0078: /**
0079: * Expect a given token; fail if the current token is different. Note that this method
0080: * does not read any tokens.
0081: *
0082: * @param token the expected token
0083: * @throws net.sf.saxon.trans.StaticError if the current token is not the expected
0084: * token
0085: */
0086:
0087: protected void expect(int token) throws StaticError {
0088: if (t.currentToken != token)
0089: grumble("expected \"" + Token.tokens[token] + "\", found "
0090: + currentTokenDisplay());
0091: }
0092:
0093: /**
0094: * Report a syntax error (a static error with error code XP0003)
0095: * @param message the error message
0096: * @exception net.sf.saxon.trans.StaticError always thrown: an exception containing the
0097: * supplied message
0098: */
0099:
0100: protected void grumble(String message) throws StaticError {
0101: grumble(message, (language == XSLT_PATTERN ? "XTSE0340"
0102: : "XPST0003"));
0103: }
0104:
0105: /**
0106: * Report a static error
0107: *
0108: * @param message the error message
0109: * @param errorCode the error code
0110: * @throws net.sf.saxon.trans.StaticError always thrown: an exception containing the
0111: * supplied message
0112: */
0113:
0114: protected void grumble(String message, String errorCode)
0115: throws StaticError {
0116: if (errorCode == null) {
0117: errorCode = "XPST0003";
0118: }
0119: String s = t.recentText();
0120: int line = t.getLineNumber();
0121: int column = t.getColumnNumber();
0122: String lineInfo = (line == 1 ? "" : ("on line " + line + ' '));
0123: String columnInfo = "at char " + column + ' ';
0124: String prefix = getLanguage() + " syntax error " + columnInfo
0125: + lineInfo
0126: + (message.startsWith("...") ? "near" : "in") + ' '
0127: + Err.wrap(s) + ":\n ";
0128: StaticError err = new StaticError(prefix + message);
0129: err.setErrorCode(errorCode);
0130: throw err;
0131: }
0132:
0133: /**
0134: * Output a warning message
0135: */
0136:
0137: protected void warning(String message) throws StaticError {
0138: String s = t.recentText();
0139: int line = t.getLineNumber();
0140: String lineInfo = (line == 1 ? "" : ("on line " + line + ' '));
0141: String prefix = "Warning " + lineInfo
0142: + (message.startsWith("...") ? "near" : "in") + ' '
0143: + Err.wrap(s) + ":\n ";
0144: env.issueWarning(prefix + message, null);
0145: }
0146:
0147: /**
0148: * Get the current language (XPath or XQuery)
0149: */
0150:
0151: protected String getLanguage() {
0152: switch (language) {
0153: case XPATH:
0154: return "XPath";
0155: case XSLT_PATTERN:
0156: return "XSLT Pattern";
0157: case SEQUENCE_TYPE:
0158: return "SequenceType";
0159: case XQUERY:
0160: return "XQuery";
0161: default:
0162: return "XPath";
0163: }
0164: }
0165:
0166: /**
0167: * Display the current token in an error message
0168: *
0169: * @return the display representation of the token
0170: */
0171: protected String currentTokenDisplay() {
0172: if (t.currentToken == Token.NAME) {
0173: return "name \"" + t.currentTokenValue + '\"';
0174: } else if (t.currentToken == Token.UNKNOWN) {
0175: return "(unknown token)";
0176: } else {
0177: return '\"' + Token.tokens[t.currentToken] + '\"';
0178: }
0179: }
0180:
0181: /**
0182: * Parse a string representing an expression
0183: *
0184: * @throws net.sf.saxon.trans.StaticError if the expression contains a syntax error
0185: * @param expression the expression expressed as a String
0186: * @param start offset within the string where parsing is to start
0187: * @param terminator character to treat as terminating the expression
0188: * @param lineNumber location of the start of the expression, for diagnostics
0189: * @param env the static context for the expression
0190: * @return an Expression object representing the result of parsing
0191: */
0192:
0193: public Expression parse(String expression, int start,
0194: int terminator, int lineNumber, StaticContext env)
0195: throws StaticError {
0196: // System.err.println("Parse expression: " + expression);
0197: this .env = env;
0198: this .nameChecker = env.getConfiguration().getNameChecker();
0199: t = new Tokenizer();
0200: try {
0201: t.tokenize(expression, start, -1, lineNumber);
0202: } catch (StaticError err) {
0203: grumble(err.getMessage());
0204: }
0205: Expression exp = parseExpression();
0206: if (t.currentToken != terminator) {
0207: if (t.currentToken == Token.EOF
0208: && terminator == Token.RCURLY) {
0209: grumble(
0210: "Missing curly brace after expression in attribute value template",
0211: "XTSE0350");
0212: } else {
0213: grumble("Unexpected token " + currentTokenDisplay()
0214: + " beyond end of expression");
0215: }
0216: }
0217: return exp;
0218: }
0219:
0220: /**
0221: * Parse a string representing an XSLT pattern
0222: *
0223: * @throws net.sf.saxon.trans.StaticError if the pattern contains a syntax error
0224: * @param pattern the pattern expressed as a String
0225: * @param env the static context for the pattern
0226: * @return a Pattern object representing the result of parsing
0227: */
0228:
0229: public Pattern parsePattern(String pattern, StaticContext env)
0230: throws StaticError {
0231: //System.err.println("Parse pattern: " + pattern);
0232: this .env = env;
0233: this .nameChecker = env.getConfiguration().getNameChecker();
0234: language = XSLT_PATTERN;
0235: t = new Tokenizer();
0236: try {
0237: t.tokenize(pattern, 0, -1, env.getLineNumber());
0238: } catch (StaticError err) {
0239: grumble(err.getMessage());
0240: }
0241: Pattern pat = parseUnionPattern();
0242: if (t.currentToken != Token.EOF)
0243: grumble("Unexpected token " + currentTokenDisplay()
0244: + " beyond end of pattern");
0245: //pat.setStaticContext(env);
0246: //System.err.println("Parsed [" + pattern + "] to " + pat.getClass() + " default prio = " + pat.getDefaultPriority());
0247: return pat;
0248: }
0249:
0250: /**
0251: * Parse a string representing a sequence type
0252: *
0253: * @param input the string, which should conform to the XPath SequenceType
0254: * production
0255: * @param env the static context
0256: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0257: * @return a SequenceType object representing the type
0258: */
0259:
0260: public SequenceType parseSequenceType(String input,
0261: StaticContext env) throws StaticError {
0262: this .env = env;
0263: this .nameChecker = env.getConfiguration().getNameChecker();
0264: language = SEQUENCE_TYPE;
0265: t = new Tokenizer();
0266: try {
0267: t.tokenize(input, 0, -1, 1);
0268: } catch (StaticError err) {
0269: grumble(err.getMessage());
0270: }
0271: SequenceType req = parseSequenceType();
0272: if (t.currentToken != Token.EOF) {
0273: grumble("Unexpected token " + currentTokenDisplay()
0274: + " beyond end of SequenceType");
0275: }
0276: return req;
0277: }
0278:
0279: //////////////////////////////////////////////////////////////////////////////////
0280: // EXPRESSIONS //
0281: //////////////////////////////////////////////////////////////////////////////////
0282:
0283: /**
0284: * Parse a top-level Expression:
0285: * ExprSingle ( ',' ExprSingle )*
0286: *
0287: * @throws net.sf.saxon.trans.StaticError if the expression contains a syntax error
0288: * @return the Expression object that results from parsing
0289: */
0290:
0291: protected Expression parseExpression() throws StaticError {
0292: Expression exp = parseExprSingle();
0293: while (t.currentToken == Token.COMMA) {
0294: nextToken();
0295: exp = Block.makeBlock(exp, parseExpression());
0296: setLocation(exp);
0297: }
0298: return exp;
0299: }
0300:
0301: /**
0302: * Parse an ExprSingle
0303: *
0304: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0305: * @return the resulting subexpression
0306: */
0307:
0308: protected Expression parseExprSingle() throws StaticError {
0309: switch (t.currentToken) {
0310: case Token.FOR:
0311: case Token.LET: // XQuery only
0312: return parseForExpression();
0313: case Token.SOME:
0314: case Token.EVERY:
0315: return parseQuantifiedExpression();
0316: case Token.IF:
0317: return parseIfExpression();
0318: case Token.TYPESWITCH:
0319: return parseTypeswitchExpression();
0320: case Token.VALIDATE:
0321: case Token.VALIDATE_STRICT:
0322: case Token.VALIDATE_LAX:
0323: return parseValidateExpression();
0324: case Token.PRAGMA:
0325: return parseExtensionExpression();
0326:
0327: default:
0328: return parseOrExpression();
0329: }
0330: }
0331:
0332: /**
0333: * Parse a Typeswitch Expression.
0334: * This construct is XQuery-only, so the XPath version of this
0335: * method throws an error unconditionally
0336: */
0337:
0338: protected Expression parseTypeswitchExpression() throws StaticError {
0339: grumble("typeswitch is not allowed in XPath");
0340: return null;
0341: }
0342:
0343: /**
0344: * Parse a Validate Expression.
0345: * This construct is XQuery-only, so the XPath version of this
0346: * method throws an error unconditionally
0347: */
0348:
0349: protected Expression parseValidateExpression() throws StaticError {
0350: grumble("validate{} expressions are not allowed in XPath");
0351: return null;
0352: }
0353:
0354: /**
0355: * Parse an Extension Expression
0356: * This construct is XQuery-only, so the XPath version of this
0357: * method throws an error unconditionally
0358: */
0359:
0360: protected Expression parseExtensionExpression() throws StaticError {
0361: grumble("extension expressions (#...#) are not allowed in XPath");
0362: return null;
0363: }
0364:
0365: /**
0366: * Parse an OrExpression:
0367: * AndExpr ( 'or' AndExpr )*
0368: *
0369: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0370: * @return the resulting subexpression
0371: */
0372:
0373: private Expression parseOrExpression() throws StaticError {
0374: Expression exp = parseAndExpression();
0375: while (t.currentToken == Token.OR) {
0376: nextToken();
0377: exp = new BooleanExpression(exp, Token.OR,
0378: parseAndExpression());
0379: setLocation(exp);
0380: }
0381: return exp;
0382: }
0383:
0384: /**
0385: * Parse an AndExpr:
0386: * EqualityExpr ( 'and' EqualityExpr )*
0387: *
0388: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0389: * @return the resulting subexpression
0390: */
0391:
0392: private Expression parseAndExpression() throws StaticError {
0393: Expression exp = parseComparisonExpression();
0394: while (t.currentToken == Token.AND) {
0395: nextToken();
0396: exp = new BooleanExpression(exp, Token.AND,
0397: parseComparisonExpression());
0398: setLocation(exp);
0399: }
0400: return exp;
0401: }
0402:
0403: /**
0404: * Parse a FOR expression:
0405: * for $x in expr (',' $y in expr)* 'return' expr
0406: *
0407: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0408: * @return the resulting subexpression
0409: */
0410:
0411: protected Expression parseForExpression() throws StaticError {
0412: if (t.currentToken == Token.LET) {
0413: grumble("'let' is not supported in XPath");
0414: }
0415: return parseMappingExpression();
0416: }
0417:
0418: /**
0419: * Parse a quantified expression:
0420: * (some|every) $x in expr 'satisfies' expr
0421: *
0422: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0423: * @return the resulting subexpression
0424: */
0425:
0426: private Expression parseQuantifiedExpression() throws StaticError {
0427: return parseMappingExpression();
0428: }
0429:
0430: /**
0431: * Parse a mapping expression. This is a common routine that handles
0432: * XPath for expressions and quantified expressions.
0433: *
0434: * <p>Syntax: <br/>
0435: * (for|some|every) $x in expr (',' $y in expr)* (return|satisfies) expr
0436: * </p>
0437: *
0438: * <p>On entry, the current token indicates whether a for, some, or every
0439: * expression is expected.</p>
0440: *
0441: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0442: * @return the resulting subexpression
0443: */
0444:
0445: protected Expression parseMappingExpression() throws StaticError {
0446: int offset = t.currentTokenStartOffset;
0447: int operator = t.currentToken;
0448: List clauseList = new ArrayList(3);
0449: do {
0450: ForClause clause = new ForClause();
0451: clause.offset = offset;
0452: clause.requiredType = SequenceType.SINGLE_ITEM;
0453: clauseList.add(clause);
0454: nextToken();
0455: expect(Token.DOLLAR);
0456: nextToken();
0457: expect(Token.NAME);
0458: String var = t.currentTokenValue;
0459:
0460: // declare the range variable
0461: RangeVariableDeclaration v = new RangeVariableDeclaration();
0462: v.setNameCode(makeNameCode(var, false));
0463: v.setRequiredType(SequenceType.SINGLE_ITEM);
0464: v.setVariableName(var);
0465: clause.rangeVariable = v;
0466: nextToken();
0467:
0468: if (isKeyword("as") && "XQuery".equals(getLanguage())) {
0469: nextToken();
0470: SequenceType type = parseSequenceType();
0471: clause.requiredType = type;
0472: v.setRequiredType(type);
0473: if (type.getCardinality() != StaticProperty.EXACTLY_ONE) {
0474: grumble(
0475: "Cardinality of range variable must be exactly one",
0476: "XPTY0004");
0477: }
0478: }
0479:
0480: // "at" clauses are not recognized in XPath
0481: clause.positionVariable = null;
0482:
0483: // process the "in" clause
0484: expect(Token.IN);
0485: nextToken();
0486: clause.sequence = parseExprSingle();
0487: declareRangeVariable(clause.rangeVariable);
0488:
0489: } while (t.currentToken == Token.COMMA);
0490:
0491: // process the "return/satisfies" expression (called the "action")
0492: if (operator == Token.FOR) {
0493: expect(Token.RETURN);
0494: } else {
0495: expect(Token.SATISFIES);
0496: }
0497: nextToken();
0498: Expression action = parseExprSingle();
0499:
0500: // work back through the list of range variables, fixing up all references
0501: // to the variables in the inner expression
0502:
0503: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
0504: for (int i = clauseList.size() - 1; i >= 0; i--) {
0505: ForClause fc = (ForClause) clauseList.get(i);
0506: Assignation exp;
0507: if (operator == Token.FOR) {
0508: exp = new ForExpression();
0509: } else {
0510: exp = new QuantifiedExpression();
0511: ((QuantifiedExpression) exp).setOperator(operator);
0512: }
0513: setLocation(exp, offset);
0514: exp.setVariableDeclaration(fc.rangeVariable);
0515: exp.setSequence(fc.sequence);
0516:
0517: // Attempt to give the range variable a more precise type, base on analysis of the
0518: // "action" expression. This will often be approximate, because variables and function
0519: // calls in the action expression have not yet been resolved. We rely on the ability
0520: // of all expressions to return some kind of type information even if this is
0521: // imprecise.
0522:
0523: if (fc.requiredType == SequenceType.SINGLE_ITEM) {
0524: SequenceType type = SequenceType.makeSequenceType(
0525: fc.sequence.getItemType(th),
0526: StaticProperty.EXACTLY_ONE);
0527: fc.rangeVariable.setRequiredType(type);
0528: } else {
0529: fc.rangeVariable.setRequiredType(fc.requiredType);
0530: }
0531: exp.setAction(action);
0532:
0533: // for the next outermost "for" clause, the "action" is this ForExpression
0534: action = exp;
0535: }
0536:
0537: // undeclare all the range variables
0538:
0539: for (int i = clauseList.size() - 1; i >= 0; i--) {
0540: undeclareRangeVariable();
0541: }
0542: //action = makeTracer(offset, action, Location.FOR_EXPRESSION, -1);
0543: return action;
0544: }
0545:
0546: /**
0547: * Parse an IF expression:
0548: * if '(' expr ')' 'then' expr 'else' expr
0549: *
0550: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0551: * @return the resulting subexpression
0552: */
0553:
0554: private Expression parseIfExpression() throws StaticError {
0555: // left paren already read
0556: int ifoffset = t.currentTokenStartOffset;
0557: nextToken();
0558: Expression condition = parseExpression();
0559: expect(Token.RPAR);
0560: nextToken();
0561: int thenoffset = t.currentTokenStartOffset;
0562: expect(Token.THEN);
0563: nextToken();
0564: Expression thenExp = makeTracer(thenoffset, parseExpression(),
0565: Location.THEN_EXPRESSION, -1);
0566: int elseoffset = t.currentTokenStartOffset;
0567: expect(Token.ELSE);
0568: nextToken();
0569: Expression elseExp = makeTracer(elseoffset, parseExprSingle(),
0570: Location.ELSE_EXPRESSION, -1);
0571: Expression ifExp = new IfExpression(condition, thenExp, elseExp);
0572: setLocation(ifExp, ifoffset);
0573: return makeTracer(ifoffset, ifExp, Location.IF_EXPRESSION, -1);
0574: }
0575:
0576: /**
0577: * Parse an "instance of" expression
0578: * Expr ("instance" "of") SequenceType
0579: *
0580: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0581: * @return the resulting subexpression
0582: */
0583:
0584: private Expression parseInstanceOfExpression() throws StaticError {
0585: Expression exp = parseTreatExpression();
0586: if (t.currentToken == Token.INSTANCE_OF) {
0587: nextToken();
0588: exp = new InstanceOfExpression(exp, parseSequenceType());
0589: setLocation(exp);
0590: }
0591: return exp;
0592: }
0593:
0594: /**
0595: * Parse a "treat as" expression
0596: * castable-expression ("treat" "as" SequenceType )?
0597: *
0598: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0599: * @return the resulting subexpression
0600: */
0601:
0602: private Expression parseTreatExpression() throws StaticError {
0603: Expression exp = parseCastableExpression();
0604: if (t.currentToken == Token.TREAT_AS) {
0605: nextToken();
0606: SequenceType target = parseSequenceType();
0607: exp = TreatExpression.make(exp, target);
0608: setLocation(exp);
0609: }
0610: return exp;
0611: }
0612:
0613: /**
0614: * Parse a "castable as" expression
0615: * Expr "castable as" AtomicType "?"?
0616: *
0617: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0618: * @return the resulting subexpression
0619: */
0620:
0621: private Expression parseCastableExpression() throws StaticError {
0622: Expression exp = parseCastExpression();
0623: if (t.currentToken == Token.CASTABLE_AS) {
0624: nextToken();
0625: expect(Token.NAME);
0626: AtomicType at = getAtomicType(t.currentTokenValue);
0627: if (at.getFingerprint() == StandardNames.XDT_ANY_ATOMIC_TYPE) {
0628: grumble("No value is castable to xdt:anyAtomicType",
0629: "XPST0080");
0630: }
0631: if (at.getFingerprint() == StandardNames.XS_NOTATION) {
0632: grumble("No value is castable to xs:NOTATION",
0633: "XPST0080");
0634: }
0635: nextToken();
0636: boolean allowEmpty = (t.currentToken == Token.QMARK);
0637: if (allowEmpty) {
0638: nextToken();
0639: }
0640: exp = new CastableExpression(exp, at, allowEmpty);
0641: setLocation(exp);
0642: }
0643: return exp;
0644: }
0645:
0646: /**
0647: * Parse a "cast as" expression
0648: * castable-expression ("cast" "as" AtomicType "?"?)
0649: *
0650: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0651: * @return the resulting subexpression
0652: */
0653:
0654: private Expression parseCastExpression() throws StaticError {
0655: Expression exp = parseUnaryExpression();
0656: if (t.currentToken == Token.CAST_AS) {
0657: nextToken();
0658: expect(Token.NAME);
0659: AtomicType at = getAtomicType(t.currentTokenValue);
0660: if (at.getFingerprint() == StandardNames.XDT_ANY_ATOMIC_TYPE) {
0661: grumble("Cannot cast to xdt:anyAtomicType", "XPST0080");
0662: }
0663: if (at.getFingerprint() == StandardNames.XS_NOTATION) {
0664: grumble("Cannot cast to xs:NOTATION", "XPST0080");
0665: }
0666: nextToken();
0667: boolean allowEmpty = (t.currentToken == Token.QMARK);
0668: if (allowEmpty) {
0669: nextToken();
0670: }
0671: exp = new CastExpression(exp, at, allowEmpty);
0672: // A QName or NOTATION constructor function must be evaluated now, while we know the namespace context
0673: final TypeHierarchy th = env.getNamePool()
0674: .getTypeHierarchy();
0675: if (((AtomicType) exp.getItemType(th))
0676: .isNamespaceSensitive()) {
0677: try {
0678: return ((CastExpression) exp).doQNameCast(env);
0679: } catch (XPathException e) {
0680: grumble(e.getMessage());
0681: }
0682: }
0683: setLocation(exp);
0684: }
0685: return exp;
0686: }
0687:
0688: /**
0689: * Analyze a token whose expected value is the name of an atomic type,
0690: * and return the object representing the atomic type.
0691: * @param qname The lexical QName of the atomic type
0692: * @return The atomic type
0693: * @throws net.sf.saxon.trans.StaticError if the QName is invalid or if no atomic type of that
0694: * name exists as a built-in type or a type in an imported schema
0695: */
0696: private AtomicType getAtomicType(String qname) throws StaticError {
0697: if (scanOnly) {
0698: return Type.STRING_TYPE;
0699: }
0700: try {
0701: String[] parts = nameChecker.getQNameParts(qname);
0702: String uri;
0703: if ("".equals(parts[0])) {
0704: short uriCode = env.getDefaultElementNamespace();
0705: uri = env.getNamePool().getURIFromURICode(uriCode);
0706: } else {
0707: try {
0708: uri = env.getURIForPrefix(parts[0]);
0709: } catch (XPathException err) {
0710: grumble(err.getMessage());
0711: uri = "";
0712: }
0713: }
0714:
0715: boolean builtInNamespace = uri
0716: .equals(NamespaceConstant.SCHEMA);
0717: if (!builtInNamespace
0718: && NamespaceConstant.isXDTNamespace(uri)) {
0719: uri = NamespaceConstant.XDT;
0720: builtInNamespace = true;
0721: }
0722:
0723: if (builtInNamespace) {
0724: ItemType t = Type.getBuiltInItemType(uri, parts[1]);
0725: if (t == null) {
0726: grumble("Unknown atomic type " + qname, "XPST0051");
0727: }
0728: if (t instanceof BuiltInAtomicType) {
0729: return (AtomicType) t;
0730: } else {
0731: grumble("The type " + qname + " is not atomic",
0732: "XPST0051");
0733: }
0734: } else if (uri.equals(NamespaceConstant.JAVA_TYPE)) {
0735: Class theClass = null;
0736: try {
0737: String className = parts[1].replace('-', '$');
0738: theClass = env.getConfiguration().getClass(
0739: className, false, null);
0740: } catch (XPathException err) {
0741: grumble("Unknown Java class " + parts[1]);
0742: }
0743: return new ExternalObjectType(theClass);
0744: } else {
0745: if (env.isImportedSchema(uri)) {
0746: int fp = env.getNamePool().getFingerprint(uri,
0747: parts[1]);
0748: if (fp == -1) {
0749: grumble("Unknown type " + qname);
0750: }
0751: SchemaType st = env.getConfiguration()
0752: .getSchemaType(fp);
0753: if (st == null) {
0754: grumble("Unknown atomic type " + qname);
0755: } else if (st instanceof AtomicType) {
0756: return (AtomicType) st;
0757: } else if (st.isComplexType()) {
0758: grumble("Type (" + qname
0759: + ") is a complex type");
0760: return null;
0761: } else {
0762: grumble("Type (" + qname
0763: + ") is a list or union type");
0764: return null;
0765: }
0766:
0767: } else {
0768: if ("".equals(uri)) {
0769: grumble("There is no imported schema for the null namespace");
0770: } else {
0771: grumble("There is no imported schema for namespace "
0772: + uri);
0773: }
0774: return null;
0775: }
0776: }
0777: grumble("Unknown atomic type " + qname);
0778: } catch (QNameException err) {
0779: grumble(err.getMessage());
0780: }
0781: return null;
0782: }
0783:
0784: /**
0785: * Parse a ComparisonExpr:<br>
0786: * RangeExpr ( op RangeExpr )*
0787: * where op is one of =, <, >, !=, <=, >=,
0788: * eq, lt, gt, ne, le, ge,
0789: * is, isnot, <<, >>
0790: *
0791: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0792: * @return the resulting subexpression
0793: */
0794:
0795: private Expression parseComparisonExpression() throws StaticError {
0796: Expression exp = parseRangeExpression();
0797: switch (t.currentToken) {
0798: case Token.IS:
0799: case Token.PRECEDES:
0800: case Token.FOLLOWS:
0801: int op = t.currentToken;
0802: nextToken();
0803: exp = new IdentityComparison(exp, op,
0804: parseRangeExpression());
0805: setLocation(exp);
0806: return exp;
0807:
0808: case Token.EQUALS:
0809: case Token.NE:
0810: case Token.LT:
0811: case Token.GT:
0812: case Token.LE:
0813: case Token.GE:
0814: op = t.currentToken;
0815: nextToken();
0816: exp = env.getConfiguration().getOptimizer()
0817: .makeGeneralComparison(exp, op,
0818: parseRangeExpression(),
0819: env.isInBackwardsCompatibleMode());
0820: setLocation(exp);
0821: return exp;
0822:
0823: case Token.FEQ:
0824: case Token.FNE:
0825: case Token.FLT:
0826: case Token.FGT:
0827: case Token.FLE:
0828: case Token.FGE:
0829: op = t.currentToken;
0830: nextToken();
0831: exp = new ValueComparison(exp, op, parseRangeExpression());
0832: setLocation(exp);
0833: return exp;
0834:
0835: default:
0836: return exp;
0837: }
0838: }
0839:
0840: /**
0841: * Parse a RangeExpr:<br>
0842: * AdditiveExpr ('to' AdditiveExpr )?
0843: *
0844: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0845: * @return the resulting subexpression
0846: */
0847:
0848: private Expression parseRangeExpression() throws StaticError {
0849: Expression exp = parseAdditiveExpression();
0850: if (t.currentToken == Token.TO) {
0851: nextToken();
0852: exp = new RangeExpression(exp, Token.TO,
0853: parseAdditiveExpression());
0854: setLocation(exp);
0855: }
0856: return exp;
0857: }
0858:
0859: /**
0860: * Parse the sequence type production.
0861: * Provisionally, we use the syntax (QName | node-kind "()") ( "*" | "+" | "?" )?
0862: * We also allow "element of type QName" and "attribute of type QName"
0863: * The QName must be the name of a built-in schema-defined data type.
0864: *
0865: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0866: * @return the resulting subexpression
0867: */
0868:
0869: protected SequenceType parseSequenceType() throws StaticError {
0870: ItemType primaryType;
0871: if (t.currentToken == Token.NAME) {
0872: primaryType = getAtomicType(t.currentTokenValue);
0873: nextToken();
0874: } else if (t.currentToken == Token.NODEKIND) {
0875: if (t.currentTokenValue == "item") {
0876: nextToken();
0877: expect(Token.RPAR);
0878: nextToken();
0879: primaryType = AnyItemType.getInstance();
0880: } else if (t.currentTokenValue == "empty-sequence") {
0881: nextToken();
0882: expect(Token.RPAR);
0883: nextToken();
0884: return SequenceType.makeSequenceType(NoNodeTest
0885: .getInstance(), StaticProperty.EMPTY);
0886: // return before trying to read an occurrence indicator
0887: } else {
0888: primaryType = parseKindTest();
0889: }
0890: } else {
0891: grumble("Expected type name in SequenceType, found "
0892: + Token.tokens[t.currentToken]);
0893: return null;
0894: }
0895:
0896: int occurrenceFlag;
0897: switch (t.currentToken) {
0898: case Token.STAR:
0899: case Token.MULT:
0900: // "*" will be tokenized different ways depending on what precedes it
0901: occurrenceFlag = StaticProperty.ALLOWS_ZERO_OR_MORE;
0902: // Make the tokenizer ignore the occurrence indicator when classifying the next token
0903: t.currentToken = Token.RPAR;
0904: nextToken();
0905: break;
0906: case Token.PLUS:
0907: occurrenceFlag = StaticProperty.ALLOWS_ONE_OR_MORE;
0908: // Make the tokenizer ignore the occurrence indicator when classifying the next token
0909: t.currentToken = Token.RPAR;
0910: nextToken();
0911: break;
0912: case Token.QMARK:
0913: occurrenceFlag = StaticProperty.ALLOWS_ZERO_OR_ONE;
0914: // Make the tokenizer ignore the occurrence indicator when classifying the next token
0915: t.currentToken = Token.RPAR;
0916: nextToken();
0917: break;
0918: default:
0919: occurrenceFlag = StaticProperty.EXACTLY_ONE;
0920: }
0921: return SequenceType.makeSequenceType(primaryType,
0922: occurrenceFlag);
0923: }
0924:
0925: /**
0926: * Parse an AdditiveExpr:
0927: * MultiplicativeExpr ( (+|-) MultiplicativeExpr )*
0928: *
0929: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0930: * @return the resulting subexpression
0931: */
0932:
0933: private Expression parseAdditiveExpression() throws StaticError {
0934: Expression exp = parseMultiplicativeExpression();
0935: while (t.currentToken == Token.PLUS
0936: || t.currentToken == Token.MINUS) {
0937: int op = t.currentToken;
0938: nextToken();
0939: exp = new ArithmeticExpression(exp, op,
0940: parseMultiplicativeExpression());
0941: setLocation(exp);
0942: }
0943: return exp;
0944: }
0945:
0946: /**
0947: * Parse a MultiplicativeExpr:<br>
0948: * UnionExpr ( (*|div|idiv|mod) UnionExpr )*
0949: *
0950: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0951: * @return the resulting subexpression
0952: */
0953:
0954: private Expression parseMultiplicativeExpression()
0955: throws StaticError {
0956: Expression exp = parseUnionExpression();
0957: while (t.currentToken == Token.MULT
0958: || t.currentToken == Token.DIV
0959: || t.currentToken == Token.IDIV
0960: || t.currentToken == Token.MOD) {
0961: int op = t.currentToken;
0962: nextToken();
0963: exp = new ArithmeticExpression(exp, op,
0964: parseUnionExpression());
0965: setLocation(exp);
0966: }
0967: return exp;
0968: }
0969:
0970: /**
0971: * Parse a UnaryExpr:<br>
0972: * ('+'|'-')* ValueExpr
0973: * parsed as ('+'|'-')? UnaryExpr
0974: *
0975: * @throws net.sf.saxon.trans.StaticError if any error is encountered
0976: * @return the resulting subexpression
0977: */
0978:
0979: private Expression parseUnaryExpression() throws StaticError {
0980: Expression exp;
0981: switch (t.currentToken) {
0982: case Token.MINUS:
0983: nextToken();
0984: exp = new ArithmeticExpression(new IntegerValue(0),
0985: Token.NEGATE, parseUnaryExpression());
0986: break;
0987: case Token.PLUS:
0988: nextToken();
0989: // Unary plus: can't ignore it completely, it might be a type error, or it might
0990: // force conversion to a number which would affect operations such as "=".
0991: exp = new ArithmeticExpression(new IntegerValue(0),
0992: Token.PLUS, parseUnaryExpression());
0993: break;
0994: case Token.VALIDATE:
0995: case Token.VALIDATE_STRICT:
0996: case Token.VALIDATE_LAX:
0997: exp = parseValidateExpression();
0998: break;
0999: case Token.PRAGMA:
1000: exp = parseExtensionExpression();
1001: break;
1002: default:
1003: exp = parsePathExpression();
1004: }
1005: setLocation(exp);
1006: return exp;
1007: }
1008:
1009: /**
1010: * Parse a UnionExpr:<br>
1011: * IntersectExceptExpr ( "|" | "union" IntersectExceptExpr )*
1012: *
1013: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1014: * @return the resulting subexpression
1015: */
1016:
1017: private Expression parseUnionExpression() throws StaticError {
1018: Expression exp = parseIntersectExpression();
1019: while (t.currentToken == Token.UNION) {
1020: nextToken();
1021: exp = new VennExpression(exp, Token.UNION,
1022: parseIntersectExpression());
1023: setLocation(exp);
1024: }
1025: return exp;
1026: }
1027:
1028: /**
1029: * Parse an IntersectExceptExpr:<br>
1030: * PathExpr ( ( 'intersect' | 'except') PathExpr )*
1031: *
1032: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1033: * @return the resulting subexpression
1034: */
1035:
1036: private Expression parseIntersectExpression() throws StaticError {
1037: Expression exp = parseInstanceOfExpression();
1038: while (t.currentToken == Token.INTERSECT
1039: || t.currentToken == Token.EXCEPT) {
1040: int op = t.currentToken;
1041: nextToken();
1042: exp = new VennExpression(exp, op,
1043: parseInstanceOfExpression());
1044: setLocation(exp);
1045: }
1046: return exp;
1047: }
1048:
1049: /**
1050: * Test whether the current token is one that can start a RelativePathExpression
1051: *
1052: * @return the resulting subexpression
1053: */
1054:
1055: private boolean atStartOfRelativePath() {
1056: switch (t.currentToken) {
1057: case Token.AXIS:
1058: case Token.AT:
1059: case Token.NAME:
1060: case Token.PREFIX:
1061: case Token.SUFFIX:
1062: case Token.STAR:
1063: case Token.NODEKIND:
1064: case Token.DOT:
1065: case Token.DOTDOT:
1066: case Token.FUNCTION:
1067: case Token.STRING_LITERAL:
1068: case Token.NUMBER:
1069: case Token.LPAR:
1070: return true;
1071: default:
1072: return false;
1073: }
1074: }
1075:
1076: /**
1077: * Parse a PathExpresssion. This includes "true" path expressions such as A/B/C, and also
1078: * constructs that may start a path expression such as a variable reference $name or a
1079: * parenthesed expression (A|B). Numeric and string literals also come under this heading.
1080: *
1081: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1082: * @return the resulting subexpression
1083: */
1084:
1085: private Expression parsePathExpression() throws StaticError {
1086: switch (t.currentToken) {
1087: case Token.SLASH:
1088: nextToken();
1089: final RootExpression start = new RootExpression();
1090: setLocation(start);
1091: if (atStartOfRelativePath()) {
1092: //final Expression path = new PathExpression(start, parseRelativePath(null));
1093: final Expression path = parseRemainingPath(start);
1094: setLocation(path);
1095: return path;
1096: } else {
1097: return start;
1098: }
1099:
1100: case Token.SLSL:
1101: // The logic for absolute path expressions changed in 8.4 so that //A/B/C parses to
1102: // (((root()/descendant-or-self::node())/A)/B)/C rather than
1103: // (root()/descendant-or-self::node())/(((A)/B)/C) as previously. This is to allow
1104: // the subsequent //A optimization to kick in.
1105: nextToken();
1106: // add in the implicit descendant-or-self::node() step
1107: // final RootExpression start2 = new RootExpression();
1108: // setLocation(start2);
1109: // final AxisExpression axisExp = new AxisExpression(Axis.DESCENDANT_OR_SELF, null);
1110: // setLocation(axisExp);
1111: // final PathExpression pathExp = new PathExpression(axisExp, parseRelativePath(null));
1112: // setLocation(pathExp);
1113: // final Expression exp = new PathExpression(start2, pathExp);
1114: // setLocation(exp);
1115: // return exp;
1116: final RootExpression start2 = new RootExpression();
1117: setLocation(start2);
1118: final AxisExpression axisExp = new AxisExpression(
1119: Axis.DESCENDANT_OR_SELF, null);
1120: setLocation(axisExp);
1121: final Expression exp = parseRemainingPath(new PathExpression(
1122: start2, axisExp));
1123: setLocation(exp);
1124: return exp;
1125: default:
1126: return parseRelativePath();
1127: }
1128:
1129: }
1130:
1131: /**
1132: * Parse a relative path (a sequence of steps). Called when the current token immediately
1133: * follows a separator (/ or //), or an implicit separator (XYZ is equivalent to ./XYZ)
1134: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1135: * @return the resulting subexpression
1136: */
1137:
1138: protected Expression parseRelativePath() throws StaticError {
1139: Expression exp = parseStepExpression();
1140: while (t.currentToken == Token.SLASH
1141: || t.currentToken == Token.SLSL) {
1142: int op = t.currentToken;
1143: nextToken();
1144: Expression next = parseStepExpression();
1145: if (op == Token.SLASH) {
1146: exp = new PathExpression(exp, next);
1147: } else {
1148: // add implicit descendant-or-self::node() step
1149: exp = new PathExpression(exp, new PathExpression(
1150: new AxisExpression(Axis.DESCENDANT_OR_SELF,
1151: null), next));
1152: }
1153: setLocation(exp);
1154: }
1155: return exp;
1156: }
1157:
1158: /**
1159: * Parse the remaining steps of an absolute path expression (one starting in "/" or "//"). Note that the
1160: * token immediately after the "/" or "//" has already been read, and in the case of "/", it has been confirmed
1161: * that we have a path expression starting with "/" rather than a standalone "/" expression.
1162: * @param start the initial implicit expression: root() in the case of "/", root()/descendant-or-self::node in
1163: * the case of "//"
1164: * @return the completed path expression
1165: * @throws StaticError
1166: */
1167: protected Expression parseRemainingPath(Expression start)
1168: throws StaticError {
1169: Expression exp = start;
1170: int op = Token.SLASH;
1171: while (true) {
1172: Expression next = parseStepExpression();
1173: if (op == Token.SLASH) {
1174: exp = new PathExpression(exp, next);
1175: } else {
1176: // add implicit descendant-or-self::node() step
1177: exp = new PathExpression(exp, new PathExpression(
1178: new AxisExpression(Axis.DESCENDANT_OR_SELF,
1179: null), next));
1180: }
1181: setLocation(exp);
1182: op = t.currentToken;
1183: if (op != Token.SLASH && op != Token.SLSL) {
1184: break;
1185: }
1186: nextToken();
1187: }
1188: return exp;
1189: }
1190:
1191: /**
1192: * Parse a step (including an optional sequence of predicates)
1193: *
1194: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1195: * @return the resulting subexpression
1196: */
1197:
1198: protected Expression parseStepExpression() throws StaticError {
1199: Expression step = parseBasicStep();
1200:
1201: // When the filter is applied to an Axis step, the nodes are considered in
1202: // axis order. In all other cases they are considered in document order
1203: boolean reverse = (step instanceof AxisExpression)
1204: && Axis.isReverse[((AxisExpression) step).getAxis()]
1205: && ((AxisExpression) step).getAxis() != Axis.SELF;
1206:
1207: while (t.currentToken == Token.LSQB) {
1208: nextToken();
1209: Expression predicate = parseExpression();
1210: expect(Token.RSQB);
1211: nextToken();
1212: step = new FilterExpression(step, predicate, env);
1213: setLocation(step);
1214: }
1215: if (reverse) {
1216: return new Reverser(step);
1217: } else {
1218: return step;
1219: }
1220: }
1221:
1222: /**
1223: * Parse a basic step expression (without the predicates)
1224: *
1225: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1226: * @return the resulting subexpression
1227: */
1228:
1229: private Expression parseBasicStep() throws StaticError {
1230: switch (t.currentToken) {
1231: case Token.DOLLAR:
1232: nextToken();
1233: expect(Token.NAME);
1234: String var = t.currentTokenValue;
1235: nextToken();
1236:
1237: if (scanOnly) {
1238: return new ContextItemExpression();
1239: // don't do any semantic checks during a prescan
1240: }
1241:
1242: int vtest = makeNameCode(var, false) & 0xfffff;
1243:
1244: // See if it's a range variable or a variable in the context
1245: VariableDeclaration b = findRangeVariable(vtest);
1246: VariableReference ref;
1247: if (b != null) {
1248: ref = new VariableReference(b);
1249: } else {
1250: try {
1251: ref = env.bindVariable(vtest);
1252: } catch (XPathException err) {
1253: grumble("Variable $" + var
1254: + " has not been declared", "XPST0008");
1255: ref = null;
1256: }
1257: }
1258: setLocation(ref);
1259: return ref;
1260:
1261: case Token.LPAR:
1262: nextToken();
1263: if (t.currentToken == Token.RPAR) {
1264: nextToken();
1265: return EmptySequence.getInstance();
1266: }
1267: Expression seq = parseExpression();
1268: expect(Token.RPAR);
1269: nextToken();
1270: return seq;
1271:
1272: case Token.STRING_LITERAL:
1273: StringValue literal = makeStringLiteral(t.currentTokenValue);
1274: nextToken();
1275: return literal;
1276:
1277: case Token.NUMBER:
1278: NumericValue number = NumericValue
1279: .parseNumber(t.currentTokenValue);
1280: if (number.isNaN()) {
1281: grumble("Invalid numeric literal "
1282: + Err.wrap(t.currentTokenValue, Err.VALUE));
1283: }
1284: nextToken();
1285: return number;
1286:
1287: case Token.FUNCTION:
1288: return parseFunctionCall();
1289:
1290: case Token.DOT:
1291: nextToken();
1292: Expression cie = new ContextItemExpression();
1293: setLocation(cie);
1294: return cie;
1295:
1296: case Token.DOTDOT:
1297: nextToken();
1298: Expression pne = new ParentNodeExpression();
1299: setLocation(pne);
1300: return pne;
1301:
1302: case Token.NAME:
1303: case Token.PREFIX:
1304: case Token.SUFFIX:
1305: case Token.STAR:
1306: case Token.NODEKIND:
1307: byte defaultAxis = Axis.CHILD;
1308: if (t.currentToken == Token.NODEKIND
1309: && t.currentTokenValue == "attribute") {
1310: defaultAxis = Axis.ATTRIBUTE;
1311: }
1312: AxisExpression ae = new AxisExpression(defaultAxis,
1313: parseNodeTest(Type.ELEMENT));
1314: setLocation(ae);
1315: return ae;
1316:
1317: case Token.AT:
1318: nextToken();
1319: switch (t.currentToken) {
1320:
1321: case Token.NAME:
1322: case Token.PREFIX:
1323: case Token.SUFFIX:
1324: case Token.STAR:
1325: case Token.NODEKIND:
1326: AxisExpression ae2 = new AxisExpression(Axis.ATTRIBUTE,
1327: parseNodeTest(Type.ATTRIBUTE));
1328: setLocation(ae2);
1329: return ae2;
1330:
1331: default:
1332: grumble("@ must be followed by a NodeTest");
1333: }
1334: break;
1335:
1336: case Token.AXIS:
1337: byte axis;
1338: try {
1339: axis = Axis.getAxisNumber(t.currentTokenValue);
1340: } catch (StaticError err) {
1341: grumble(err.getMessage());
1342: axis = Axis.CHILD; // error recovery
1343: }
1344: if (axis == Axis.NAMESPACE && language == XQUERY) {
1345: grumble("The namespace axis is not available in XQuery");
1346: }
1347: short principalNodeType = Axis.principalNodeType[axis];
1348: nextToken();
1349: switch (t.currentToken) {
1350:
1351: case Token.NAME:
1352: case Token.PREFIX:
1353: case Token.SUFFIX:
1354: case Token.STAR:
1355: case Token.NODEKIND:
1356: Expression ax = new AxisExpression(axis,
1357: parseNodeTest(principalNodeType));
1358: setLocation(ax);
1359: return ax;
1360:
1361: default:
1362: grumble("Unexpected token " + currentTokenDisplay()
1363: + " after axis name");
1364: }
1365: break;
1366:
1367: case Token.KEYWORD_CURLY:
1368: case Token.ELEMENT_QNAME:
1369: case Token.ATTRIBUTE_QNAME:
1370: case Token.PI_QNAME:
1371: case Token.TAG:
1372: return parseConstructor();
1373:
1374: default:
1375: grumble("Unexpected token " + currentTokenDisplay()
1376: + " in path expression");
1377: //break;
1378: }
1379: return null;
1380: }
1381:
1382: /**
1383: * Method to make a string literal from a token identified as a string
1384: * literal. This is trivial in XPath, but in XQuery the method is overridden
1385: * to identify pseudo-XML character and entity references. Note that the job of handling
1386: * doubled string delimiters is done by the tokenizer.
1387: * @param currentTokenValue
1388: * @return The string value of the string literal
1389: */
1390:
1391: protected StringValue makeStringLiteral(String currentTokenValue)
1392: throws StaticError {
1393: return StringValue.makeStringValue(currentTokenValue);
1394: }
1395:
1396: /**
1397: * Parse a node constructor. This is allowed only in XQuery, so the method throws
1398: * an error for XPath.
1399: */
1400:
1401: protected Expression parseConstructor() throws StaticError {
1402: grumble("Node constructor expressions are allowed only in XQuery, not in XPath");
1403: return null;
1404: }
1405:
1406: /**
1407: * Parse a NodeTest.
1408: * One of QName, prefix:*, *:suffix, *, text(), node(), comment(), or
1409: * processing-instruction(literal?), or element(~,~), attribute(~,~), etc.
1410: *
1411: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1412: * @param nodeType the node type being sought if one is specified
1413: * @return the resulting NodeTest object
1414: */
1415:
1416: protected NodeTest parseNodeTest(short nodeType) throws StaticError {
1417: int tok = t.currentToken;
1418: String tokv = t.currentTokenValue;
1419: switch (tok) {
1420: case Token.NAME:
1421: nextToken();
1422: return makeNameTest(nodeType, tokv,
1423: nodeType == Type.ELEMENT);
1424:
1425: case Token.PREFIX:
1426: nextToken();
1427: return makeNamespaceTest(nodeType, tokv);
1428:
1429: case Token.SUFFIX:
1430: nextToken();
1431: tokv = t.currentTokenValue;
1432: expect(Token.NAME);
1433: nextToken();
1434: return makeLocalNameTest(nodeType, tokv);
1435:
1436: case Token.STAR:
1437: nextToken();
1438: return NodeKindTest.makeNodeKindTest(nodeType);
1439:
1440: case Token.NODEKIND:
1441: return parseKindTest();
1442:
1443: default:
1444: grumble("Unrecognized node test");
1445: return null;
1446: }
1447: }
1448:
1449: /**
1450: * Parse a KindTest
1451: */
1452:
1453: private NodeTest parseKindTest() throws StaticError {
1454: String typeName = t.currentTokenValue;
1455: boolean schemaDeclaration = (typeName.startsWith("schema-"));
1456: int primaryType = getSystemType(typeName);
1457: int nameCode = -1;
1458: int contentType;
1459: boolean empty = false;
1460: nextToken();
1461: if (t.currentToken == Token.RPAR) {
1462: if (schemaDeclaration) {
1463: grumble("schema-element() and schema-attribute() require a name to be supplied");
1464: return null;
1465: }
1466: empty = true;
1467: nextToken();
1468: }
1469: switch (primaryType) {
1470: case Type.ITEM:
1471: grumble("item() is not allowed in a path expression");
1472: return null;
1473: case Type.NODE:
1474: if (empty) {
1475: return AnyNodeTest.getInstance();
1476: } else {
1477: grumble("No arguments are allowed in node()");
1478: return null;
1479: }
1480: case Type.TEXT:
1481: if (empty) {
1482: return NodeKindTest.TEXT;
1483: } else {
1484: grumble("No arguments are allowed in text()");
1485: return null;
1486: }
1487: case Type.COMMENT:
1488: if (empty) {
1489: return NodeKindTest.COMMENT;
1490: } else {
1491: grumble("No arguments are allowed in comment()");
1492: return null;
1493: }
1494: case Type.NAMESPACE:
1495: grumble("No node test is defined for namespace nodes");
1496: return null;
1497: case Type.DOCUMENT:
1498: if (empty) {
1499: return NodeKindTest.DOCUMENT;
1500: } else {
1501: int innerType;
1502: try {
1503: innerType = getSystemType(t.currentTokenValue);
1504: } catch (XPathException err) {
1505: innerType = Type.EMPTY;
1506: }
1507: if (innerType != Type.ELEMENT) {
1508: grumble("Argument to document-node() must be an element type descriptor");
1509: return null;
1510: }
1511: NodeTest inner = parseKindTest();
1512: expect(Token.RPAR);
1513: nextToken();
1514: return new DocumentNodeTest(inner);
1515: }
1516: case Type.PROCESSING_INSTRUCTION:
1517: if (empty) {
1518: return NodeKindTest.PROCESSING_INSTRUCTION;
1519: } else if (t.currentToken == Token.STRING_LITERAL) {
1520: try {
1521: String[] parts = nameChecker
1522: .getQNameParts(t.currentTokenValue);
1523: if ("".equals(parts[0])) {
1524: nameCode = makeNameCode(parts[1], false);
1525: } else {
1526: warning("No processing instruction name will ever contain a colon");
1527: nameCode = env
1528: .getNamePool()
1529: .allocate(
1530: "prefix",
1531: "http://saxon.sf.net/ nonexistent namespace",
1532: "___invalid-name");
1533: }
1534: } catch (QNameException e) {
1535: warning("No processing instruction will ever be named '"
1536: + t.currentTokenValue
1537: + "'. "
1538: + e.getMessage());
1539: nameCode = env
1540: .getNamePool()
1541: .allocate(
1542: "prefix",
1543: "http://saxon.sf.net/ nonexistent namespace",
1544: "___invalid-name");
1545: }
1546: } else if (t.currentToken == Token.NAME) {
1547: try {
1548: String[] parts = nameChecker
1549: .getQNameParts(t.currentTokenValue);
1550: if ("".equals(parts[0])) {
1551: nameCode = makeNameCode(parts[1], false);
1552: } else {
1553: grumble("Processing instruction name must not contain a colon");
1554: }
1555: } catch (QNameException e) {
1556: grumble("Invalid processing instruction name. "
1557: + e.getMessage());
1558: }
1559: } else {
1560: grumble("Processing instruction name must be a QName or a string literal");
1561: }
1562: nextToken();
1563: expect(Token.RPAR);
1564: nextToken();
1565: return new NameTest(Type.PROCESSING_INSTRUCTION, nameCode,
1566: env.getNamePool());
1567:
1568: case Type.ATTRIBUTE:
1569: // drop through
1570:
1571: case Type.ELEMENT:
1572: String nodeName = "";
1573: if (empty) {
1574: return NodeKindTest.makeNodeKindTest(primaryType);
1575: } else if (t.currentToken == Token.STAR
1576: || t.currentToken == Token.MULT) {
1577: // allow for both representations of "*" to be safe
1578: if (schemaDeclaration) {
1579: grumble("schema-element() and schema-attribute() must specify an actual name, not '*'");
1580: return null;
1581: }
1582: nameCode = -1;
1583: } else if (t.currentToken == Token.NAME) {
1584: nodeName = t.currentTokenValue;
1585: nameCode = makeNameCode(t.currentTokenValue,
1586: primaryType == Type.ELEMENT); // & 0xfffff;
1587: } else {
1588: grumble("Unexpected " + Token.tokens[t.currentToken]
1589: + " after '(' in SequenceType");
1590: }
1591: String suri = null;
1592: if (nameCode != -1) {
1593: suri = env.getNamePool().getURI(nameCode);
1594: }
1595: nextToken();
1596: if (t.currentToken == Token.SLASH) {
1597: grumble("Schema context paths have been dropped from the language specification");
1598: } else if (t.currentToken == Token.RPAR) {
1599: nextToken();
1600: if (nameCode == -1) {
1601: // element(*) or attribute(*)
1602: return NodeKindTest.makeNodeKindTest(primaryType);
1603: } else {
1604: NodeTest nameTest = null;
1605: SchemaType schemaType = null;
1606: if (primaryType == Type.ATTRIBUTE) {
1607: // attribute(N) or schema-attribute(N)
1608: if (schemaDeclaration) {
1609: SchemaDeclaration attributeDecl = env
1610: .getConfiguration()
1611: .getAttributeDeclaration(
1612: nameCode & 0xfffff);
1613: if (!env.isImportedSchema(suri)) {
1614: grumble("No schema has been imported for namespace '"
1615: + suri + '\'');
1616: }
1617: if (attributeDecl == null) {
1618: grumble("There is no declaration for attribute @"
1619: + nodeName
1620: + " in an imported schema");
1621: } else {
1622: schemaType = attributeDecl.getType();
1623: nameTest = new NameTest(Type.ATTRIBUTE,
1624: nameCode, env.getNamePool());
1625: }
1626: } else {
1627: nameTest = new NameTest(Type.ATTRIBUTE,
1628: nameCode, env.getNamePool());
1629: return nameTest;
1630: }
1631: } else {
1632: // element(N) or schema-element(N)
1633: if (schemaDeclaration) {
1634: SchemaDeclaration elementDecl = env
1635: .getConfiguration()
1636: .getElementDeclaration(
1637: nameCode & 0xfffff);
1638: if (!env.isImportedSchema(suri)) {
1639: grumble("No schema has been imported for namespace '"
1640: + suri + '\'');
1641: }
1642: if (elementDecl == null) {
1643: grumble("There is no declaration for element <"
1644: + nodeName
1645: + "> in an imported schema");
1646: } else {
1647: schemaType = elementDecl.getType();
1648: nameTest = env.getConfiguration()
1649: .makeSubstitutionGroupTest(
1650: elementDecl);
1651:
1652: }
1653: } else {
1654: nameTest = new NameTest(Type.ELEMENT,
1655: nameCode, env.getNamePool());
1656: return nameTest;
1657: }
1658: }
1659: ContentTypeTest contentTest = null;
1660: if (schemaType != null) {
1661: contentTest = new ContentTypeTest(primaryType,
1662: schemaType, env.getConfiguration());
1663: }
1664:
1665: if (contentTest == null) {
1666: return nameTest;
1667: } else {
1668: return new CombinedNodeTest(nameTest,
1669: Token.INTERSECT, contentTest);
1670: }
1671: }
1672: } else if (t.currentToken == Token.COMMA) {
1673: if (schemaDeclaration) {
1674: grumble("schema-element() and schema-attribute() must have one argument only");
1675: return null;
1676: }
1677: nextToken();
1678: NodeTest result;
1679: if (t.currentToken == Token.STAR) {
1680: grumble("'*' is no longer permitted as the second argument of element() and attribute()");
1681: return null;
1682: } else if (t.currentToken == Token.NAME) {
1683: SchemaType schemaType;
1684: contentType = makeNameCode(t.currentTokenValue,
1685: true) & 0xfffff;
1686: String uri = env.getNamePool().getURI(contentType);
1687: String lname = env.getNamePool().getLocalName(
1688: contentType);
1689: if (uri.equals(NamespaceConstant.SCHEMA)
1690: || NamespaceConstant.isXDTNamespace(uri)) {
1691: schemaType = env.getConfiguration()
1692: .getSchemaType(contentType);
1693: } else {
1694: if (!env.isImportedSchema(uri)) {
1695: grumble("No schema has been imported for namespace '"
1696: + uri + '\'');
1697: }
1698: schemaType = env.getConfiguration()
1699: .getSchemaType(contentType);
1700: }
1701: if (schemaType == null) {
1702: grumble("Unknown type name " + lname);
1703: }
1704: if (primaryType == Type.ATTRIBUTE
1705: && schemaType.isComplexType()) {
1706: grumble("An attribute cannot have a complex type");
1707: }
1708: ContentTypeTest typeTest = new ContentTypeTest(
1709: primaryType, schemaType, env
1710: .getConfiguration());
1711: if (nameCode == -1) {
1712: // this represents element(*,T) or attribute(*,T)
1713: result = typeTest;
1714: if (primaryType == Type.ATTRIBUTE) {
1715: nextToken();
1716: } else {
1717: // assert (primaryType == Type.ELEMENT);
1718: nextToken();
1719: if (t.currentToken == Token.QMARK) {
1720: typeTest.setNillable(true);
1721: nextToken();
1722: }
1723: }
1724: } else {
1725: if (primaryType == Type.ATTRIBUTE) {
1726: NodeTest nameTest = new NameTest(
1727: Type.ATTRIBUTE, nameCode, env
1728: .getNamePool());
1729: result = new CombinedNodeTest(nameTest,
1730: Token.INTERSECT, typeTest);
1731: nextToken();
1732: } else {
1733: // assert (primaryType == Type.ELEMENT);
1734: NodeTest nameTest = new NameTest(
1735: Type.ELEMENT, nameCode, env
1736: .getNamePool());
1737: result = new CombinedNodeTest(nameTest,
1738: Token.INTERSECT, typeTest);
1739: nextToken();
1740: if (t.currentToken == Token.QMARK) {
1741: typeTest.setNillable(true);
1742: nextToken();
1743: }
1744: }
1745: }
1746: } else {
1747: grumble("Unexpected "
1748: + Token.tokens[t.currentToken]
1749: + " after ',' in SequenceType");
1750: return null;
1751: }
1752:
1753: expect(Token.RPAR);
1754: nextToken();
1755: return result;
1756: } else {
1757: grumble("Expected ')' or ',' in SequenceType");
1758: }
1759: return null;
1760: default:
1761: // can't happen!
1762: grumble("Unknown node kind");
1763: return null;
1764: }
1765: }
1766:
1767: /**
1768: * Get a system type - that is, one whose name is a keyword rather than a QName. This includes the node
1769: * kinds such as element and attribute, the generic types node() and item(), and the pseudo-type empty-sequence()
1770: *
1771: * @param name
1772: * @exception net.sf.saxon.trans.StaticError
1773: */
1774: private static int getSystemType(String name) throws StaticError {
1775: if ("item".equals(name))
1776: return Type.ITEM;
1777: else if ("document-node".equals(name))
1778: return Type.DOCUMENT;
1779: else if ("element".equals(name))
1780: return Type.ELEMENT;
1781: else if ("schema-element".equals(name))
1782: return Type.ELEMENT;
1783: else if ("attribute".equals(name))
1784: return Type.ATTRIBUTE;
1785: else if ("schema-attribute".equals(name))
1786: return Type.ATTRIBUTE;
1787: else if ("text".equals(name))
1788: return Type.TEXT;
1789: else if ("comment".equals(name))
1790: return Type.COMMENT;
1791: else if ("processing-instruction".equals(name))
1792: return Type.PROCESSING_INSTRUCTION;
1793: else if ("namespace".equals(name))
1794: return Type.NAMESPACE;
1795: else if ("node".equals(name))
1796: return Type.NODE;
1797: else if ("empty".equals(name))
1798: return Type.EMPTY;
1799: else
1800: throw new StaticError("Unknown type " + name);
1801: }
1802:
1803: /**
1804: * Parse a function call
1805: * function-name '(' ( Expression (',' Expression )* )? ')'
1806: *
1807: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1808: * @return the resulting subexpression
1809: */
1810:
1811: private Expression parseFunctionCall() throws StaticError {
1812:
1813: String fname = t.currentTokenValue;
1814: int offset = t.currentTokenStartOffset;
1815: ArrayList args = new ArrayList(10);
1816:
1817: // the "(" has already been read by the Tokenizer: now parse the arguments
1818:
1819: nextToken();
1820: if (t.currentToken != Token.RPAR) {
1821: Expression arg = parseExprSingle();
1822: args.add(arg);
1823: while (t.currentToken == Token.COMMA) {
1824: nextToken();
1825: arg = parseExprSingle();
1826: args.add(arg);
1827: }
1828: expect(Token.RPAR);
1829: }
1830: nextToken();
1831:
1832: if (scanOnly) {
1833: return StringValue.EMPTY_STRING;
1834: }
1835:
1836: Expression[] arguments = new Expression[args.size()];
1837: args.toArray(arguments);
1838:
1839: String[] parts;
1840: try {
1841: parts = nameChecker.getQNameParts(fname);
1842: } catch (QNameException e) {
1843: grumble("Unknown prefix in function name " + fname + "()");
1844: return null;
1845: }
1846: String uri;
1847: if ("".equals(parts[0])) {
1848: uri = env.getDefaultFunctionNamespace();
1849: } else {
1850: try {
1851: uri = env.getURIForPrefix(parts[0]);
1852: } catch (XPathException err) {
1853: grumble(err.getMessage());
1854: return null;
1855: }
1856: }
1857: int nameCode = env.getNamePool().allocate(parts[0], uri,
1858: parts[1]);
1859: if (uri.equals(NamespaceConstant.SCHEMA)) {
1860: ItemType t = Type.getBuiltInItemType(uri, parts[1]);
1861: if (t instanceof BuiltInAtomicType
1862: && !env.isAllowedBuiltInType((BuiltInAtomicType) t)) {
1863: warning("The type "
1864: + fname
1865: + " is not recognized by a Basic XSLT Processor. "
1866: + "Saxon permits it for the time being.");
1867: }
1868: }
1869: Expression fcall;
1870: try {
1871: fcall = env.getFunctionLibrary().bind(nameCode, uri,
1872: parts[1], arguments);
1873: } catch (XPathException err) {
1874: if (err.getErrorCodeLocalPart() == null) {
1875: err.setErrorCode("XPST0017");
1876: }
1877: grumble(err.getMessage(), err.getErrorCodeLocalPart());
1878: return null;
1879: }
1880: if (fcall == null) {
1881: String msg = "Cannot find a matching " + arguments.length
1882: + "-argument function named "
1883: + env.getNamePool().getClarkName(nameCode) + "()";
1884: if (!env.getConfiguration().isAllowExternalFunctions()) {
1885: msg += ". Note: external function calls have been disabled";
1886: }
1887: if (env.isInBackwardsCompatibleMode()) {
1888: // treat this as a dynamic error to be reported only if the function call is executed
1889: DynamicError err = new DynamicError(msg);
1890: ErrorExpression exp = new ErrorExpression(err);
1891: setLocation(exp);
1892: return exp;
1893: }
1894: grumble(msg);
1895: }
1896: // A QName or NOTATION constructor function must be evaluated now, while we know the namespace context
1897: if (fcall instanceof CastExpression
1898: && ((AtomicType) fcall.getItemType(env.getNamePool()
1899: .getTypeHierarchy())).isNamespaceSensitive()) {
1900: try {
1901: return ((CastExpression) fcall).doQNameCast(env);
1902: } catch (XPathException e) {
1903: grumble(e.getMessage());
1904: }
1905: }
1906: setLocation(fcall, offset);
1907: for (int a = 0; a < arguments.length; a++) {
1908: ((ComputedExpression) fcall)
1909: .adoptChildExpression(arguments[a]);
1910: }
1911: return makeTracer(offset, fcall, Location.FUNCTION_CALL,
1912: nameCode);
1913:
1914: }
1915:
1916: //////////////////////////////////////////////////////////////////////////////////
1917: // Routines for handling range variables
1918: //////////////////////////////////////////////////////////////////////////////////
1919:
1920: /**
1921: * Declare a range variable (record its existence within the parser).
1922: * A range variable is a variable declared within an expression, as distinct
1923: * from a variable declared in the context.
1924: *
1925: * @param declaration the VariableDeclaration to be added to the stack
1926: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1927: */
1928:
1929: protected void declareRangeVariable(VariableDeclaration declaration)
1930: throws StaticError {
1931: if (rangeVariables == null) {
1932: rangeVariables = new Stack();
1933: }
1934: rangeVariables.push(declaration);
1935: }
1936:
1937: /**
1938: * Note when the most recently declared range variable has gone out of scope
1939: */
1940:
1941: protected void undeclareRangeVariable() {
1942: rangeVariables.pop();
1943: }
1944:
1945: /**
1946: * Locate a range variable with a given name. (By "range variable", we mean a
1947: * variable declared within the expression where it is used.)
1948: *
1949: * @param fingerprint identifies the name of the range variable within the
1950: * name pool
1951: * @return null if not found (this means the variable is probably a
1952: * context variable); otherwise the relevant VariableDeclaration
1953: */
1954:
1955: private VariableDeclaration findRangeVariable(int fingerprint) {
1956: if (rangeVariables == null) {
1957: return null;
1958: }
1959: for (int v = rangeVariables.size() - 1; v >= 0; v--) {
1960: VariableDeclaration b = (VariableDeclaration) rangeVariables
1961: .elementAt(v);
1962: if ((b.getNameCode() & 0xfffff) == fingerprint) {
1963: return b;
1964: }
1965: }
1966: return null; // not an in-scope range variable
1967: }
1968:
1969: /**
1970: * Get the range variable stack. Used when parsing a nested subexpression
1971: * inside an attribute constructor
1972: */
1973:
1974: public Stack getRangeVariableStack() {
1975: return rangeVariables;
1976: }
1977:
1978: /**
1979: * Set the range variable stack. Used when parsing a nested subexpression
1980: * inside an attribute constructor.
1981: */
1982:
1983: public void setRangeVariableStack(Stack stack) {
1984: rangeVariables = stack;
1985: }
1986:
1987: //////////////////////////////////////////////////////////////////////////////////
1988: // PATTERNS //
1989: //////////////////////////////////////////////////////////////////////////////////
1990:
1991: /**
1992: * Parse a Union Pattern:<br>
1993: * pathPattern ( | pathPattern )*
1994: *
1995: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1996: * @return the pattern that results from parsing
1997: */
1998:
1999: private Pattern parseUnionPattern() throws StaticError {
2000: Pattern exp1 = parsePathPattern();
2001:
2002: while (t.currentToken == Token.UNION) {
2003: if (t.currentTokenValue == "union") {
2004: grumble("Union operator in a pattern must be written as '|'");
2005: }
2006: nextToken();
2007: Pattern exp2 = parsePathPattern();
2008: exp1 = new UnionPattern(exp1, exp2);
2009: }
2010:
2011: return exp1;
2012: }
2013:
2014: /**
2015: * Parse a Location Path Pattern:
2016: *
2017: * @throws net.sf.saxon.trans.StaticError if any error is encountered
2018: * @return the pattern that results from parsing
2019: */
2020:
2021: private Pattern parsePathPattern() throws StaticError {
2022:
2023: Pattern prev = null;
2024: int connector = -1;
2025: boolean rootonly = false;
2026:
2027: // special handling of stuff before the first component
2028:
2029: switch (t.currentToken) {
2030: case Token.SLASH:
2031: connector = t.currentToken;
2032: nextToken();
2033: prev = new NodeTestPattern(NodeKindTest
2034: .makeNodeKindTest(Type.DOCUMENT));
2035: rootonly = true;
2036: break;
2037: case Token.SLSL: // leading double slash can't be ignored
2038: // because it changes the default priority
2039: connector = t.currentToken;
2040: nextToken();
2041: prev = new NodeTestPattern(NodeKindTest
2042: .makeNodeKindTest(Type.DOCUMENT));
2043: rootonly = false;
2044: break;
2045: default:
2046: break;
2047: }
2048:
2049: while (true) {
2050: Pattern pat = null;
2051: switch (t.currentToken) {
2052: case Token.AXIS:
2053: if ("child".equals(t.currentTokenValue)) {
2054: nextToken();
2055: pat = parsePatternStep(Type.ELEMENT);
2056: } else if ("attribute".equals(t.currentTokenValue)) {
2057: nextToken();
2058: pat = parsePatternStep(Type.ATTRIBUTE);
2059: } else {
2060: grumble("Axis in pattern must be child or attribute");
2061: }
2062: break;
2063:
2064: case Token.STAR:
2065: case Token.NAME:
2066: case Token.PREFIX:
2067: case Token.SUFFIX:
2068: pat = parsePatternStep(Type.ELEMENT);
2069: break;
2070:
2071: case Token.NODEKIND:
2072: pat = parsePatternStep(t.currentTokenValue == "attribute" ? Type.ATTRIBUTE
2073: : Type.ELEMENT);
2074: break;
2075:
2076: case Token.AT:
2077: nextToken();
2078: pat = parsePatternStep(Type.ATTRIBUTE);
2079: break;
2080:
2081: case Token.FUNCTION: // must be id(literal) or key(literal,literal)
2082: if (prev != null) {
2083: grumble("Function call may appear only at the start of a pattern");
2084: }
2085: if ("id".equals(t.currentTokenValue)) {
2086: nextToken();
2087: Expression idValue = null;
2088: if (t.currentToken == Token.STRING_LITERAL) {
2089: idValue = new StringValue(t.currentTokenValue);
2090: } else if (t.currentToken == Token.DOLLAR) {
2091: nextToken();
2092: expect(Token.NAME);
2093: int varNameCode = makeNameCode(
2094: t.currentTokenValue, false) & 0xfffff;
2095: idValue = env.bindVariable(varNameCode);
2096: } else {
2097: grumble("id value in pattern must be either a literal or a variable reference");
2098: }
2099: pat = new IDPattern(idValue);
2100: nextToken();
2101: expect(Token.RPAR);
2102: nextToken();
2103: } else if ("key".equals(t.currentTokenValue)) {
2104: nextToken();
2105: expect(Token.STRING_LITERAL);
2106: String keyname = t.currentTokenValue;
2107: nextToken();
2108: expect(Token.COMMA);
2109: nextToken();
2110: Expression idValue = null;
2111: if (t.currentToken == Token.STRING_LITERAL) {
2112: idValue = new StringValue(t.currentTokenValue);
2113: } else if (t.currentToken == Token.DOLLAR) {
2114: nextToken();
2115: expect(Token.NAME);
2116: int varNameCode = makeNameCode(
2117: t.currentTokenValue, false) & 0xfffff;
2118: idValue = env.bindVariable(varNameCode);
2119: } else {
2120: grumble("key value must be either a literal or a variable reference");
2121: }
2122: pat = new KeyPattern(makeNameCode(keyname, false),
2123: idValue);
2124: nextToken();
2125: expect(Token.RPAR);
2126: nextToken();
2127: } else {
2128: grumble("The only functions allowed in a pattern are id() and key()");
2129: }
2130: break;
2131:
2132: default:
2133: if (rootonly) { // the pattern was plain '/'
2134: return prev;
2135: }
2136: grumble("Unexpected token in pattern, found "
2137: + currentTokenDisplay());
2138: }
2139:
2140: if (prev != null) {
2141: if (connector == Token.SLASH) {
2142: ((LocationPathPattern) pat).parentPattern = prev;
2143: } else { // connector == SLSL
2144: ((LocationPathPattern) pat).ancestorPattern = prev;
2145: }
2146: }
2147: connector = t.currentToken;
2148: rootonly = false;
2149: if (connector == Token.SLASH || connector == Token.SLSL) {
2150: prev = pat;
2151: nextToken();
2152: } else {
2153: return pat;
2154: }
2155: }
2156: }
2157:
2158: /**
2159: * Parse a pattern step (after any axis name or @)
2160: *
2161: * @throws net.sf.saxon.trans.StaticError if any error is encountered
2162: * @param principalNodeType is ELEMENT if we're on the child axis, ATTRIBUTE for
2163: * the attribute axis
2164: * @return the pattern that results from parsing
2165: */
2166:
2167: private Pattern parsePatternStep(short principalNodeType)
2168: throws StaticError {
2169: LocationPathPattern step = new LocationPathPattern();
2170: NodeTest test = parseNodeTest(principalNodeType);
2171: if (test instanceof AnyNodeTest) {
2172: // handle node() and @node() specially
2173: if (principalNodeType == Type.ELEMENT) {
2174: // this means we're on the CHILD axis
2175: test = new AnyChildNodePattern();
2176: } else {
2177: // we're on the attribute axis
2178: test = NodeKindTest.makeNodeKindTest(principalNodeType);
2179: }
2180: }
2181:
2182: // Deal with nonsense patterns such as @comment() or child::attribute(). These
2183: // are legal, but will never match anything.
2184:
2185: int kind = test.getPrimitiveType();
2186: if (principalNodeType == Type.ELEMENT
2187: && (kind == Type.ATTRIBUTE || kind == Type.NAMESPACE)) {
2188: test = new NoNodeTest();
2189: } else if (principalNodeType == Type.ATTRIBUTE
2190: && (kind == Type.COMMENT || kind == Type.TEXT
2191: || kind == Type.PROCESSING_INSTRUCTION
2192: || kind == Type.ELEMENT || kind == Type.DOCUMENT)) {
2193: test = new NoNodeTest();
2194: }
2195:
2196: step.nodeTest = test;
2197: parseFilters(step);
2198: return step;
2199: }
2200:
2201: /**
2202: * Test to see if there are filters for a Pattern, if so, parse them
2203: *
2204: * @param path the LocationPathPattern to which the filters are to be
2205: * added
2206: * @throws net.sf.saxon.trans.StaticError if any error is encountered
2207: */
2208:
2209: private void parseFilters(LocationPathPattern path)
2210: throws StaticError {
2211: while (t.currentToken == Token.LSQB) {
2212: nextToken();
2213: Expression qual = parseExpression();
2214: expect(Token.RSQB);
2215: nextToken();
2216: path.addFilter(qual);
2217: }
2218: }
2219:
2220: // Helper methods to access the static context
2221:
2222: /**
2223: * Make a NameCode, using this Element as the context for namespace resolution
2224: *
2225: * @throws net.sf.saxon.trans.StaticError if the name is invalid, or the prefix
2226: * undeclared
2227: * @param qname The name as written, in the form "[prefix:]localname"
2228: * @param useDefault Defines the action when there is no prefix. If
2229: * true, use the default namespace URI for element names. If false,
2230: * use no namespace URI (as for attribute names).
2231: * @return the namecode, which can be used to identify this name in the
2232: * name pool
2233: */
2234:
2235: public final int makeNameCode(String qname, boolean useDefault)
2236: throws StaticError {
2237: if (scanOnly) {
2238: return -1;
2239: }
2240: try {
2241: String[] parts = nameChecker.getQNameParts(qname);
2242: String prefix = parts[0];
2243: if ("".equals(prefix)) {
2244: short uricode = 0;
2245: if (useDefault) {
2246: uricode = env.getDefaultElementNamespace();
2247: }
2248: return env.getNamePool().allocate(prefix, uricode,
2249: qname);
2250: } else {
2251: try {
2252: String uri = env.getURIForPrefix(prefix);
2253: return env.getNamePool().allocate(prefix, uri,
2254: parts[1]);
2255: } catch (XPathException err) {
2256: grumble(err.getMessage(), err
2257: .getErrorCodeLocalPart());
2258: return -1;
2259: }
2260: }
2261: } catch (QNameException e) {
2262: //throw new XPathException.Static(e.getMessage());
2263: grumble(e.getMessage());
2264: return -1;
2265: }
2266: }
2267:
2268: /**
2269: * Make a NameTest, using the static context for namespace resolution
2270: *
2271: * @param nodeType the type of node required (identified by a constant in
2272: * class Type)
2273: * @param qname the lexical QName of the required node
2274: * @param useDefault true if the default namespace should be used when
2275: * the QName is unprefixed
2276: * @throws net.sf.saxon.trans.StaticError if the QName is invalid
2277: * @return a NameTest, representing a pattern that tests for a node of a
2278: * given node kind and a given name
2279: */
2280:
2281: public NameTest makeNameTest(short nodeType, String qname,
2282: boolean useDefault) throws StaticError {
2283: int nameCode = makeNameCode(qname, useDefault);
2284: NameTest nt = new NameTest(nodeType, nameCode, env
2285: .getNamePool());
2286: //nt.setOriginalText(qname);
2287: return nt;
2288: }
2289:
2290: /**
2291: * Make a NamespaceTest (name:*)
2292: *
2293: * @param nodeType integer code identifying the type of node required
2294: * @param prefix the namespace prefix
2295: * @throws net.sf.saxon.trans.StaticError if the namespace prefix is not declared
2296: * @return the NamespaceTest, a pattern that matches all nodes in this
2297: * namespace
2298: */
2299:
2300: public NamespaceTest makeNamespaceTest(short nodeType, String prefix)
2301: throws StaticError {
2302: if (scanOnly) {
2303: // return an arbitrary namespace if we're only doing a syntax check
2304: return new NamespaceTest(env.getNamePool(), nodeType,
2305: NamespaceConstant.SAXON);
2306: }
2307:
2308: try {
2309: NamespaceTest nt = new NamespaceTest(env.getNamePool(),
2310: nodeType, env.getURIForPrefix(prefix));
2311: //nt.setOriginalText(prefix + ":*");
2312: return nt;
2313: } catch (XPathException e) {
2314: // env.getURIForPrefix can return a dynamic error
2315: grumble(e.getMessage());
2316: return null;
2317: }
2318: }
2319:
2320: /**
2321: * Make a LocalNameTest (*:name)
2322: *
2323: * @param nodeType the kind of node to be matched
2324: * @param localName the requred local name
2325: * @throws net.sf.saxon.trans.StaticError if the local name is invalid
2326: * @return a LocalNameTest, a pattern which matches all nodes of a given
2327: * local name, regardless of namespace
2328: */
2329:
2330: public LocalNameTest makeLocalNameTest(short nodeType,
2331: String localName) throws StaticError {
2332: if (!nameChecker.isValidNCName(localName)) {
2333: grumble("Local name [" + localName
2334: + "] contains invalid characters");
2335: }
2336: return new LocalNameTest(env.getNamePool(), nodeType, localName);
2337: }
2338:
2339: /**
2340: * Set location information on an expression. At present this consists of a simple
2341: * line number. Needed mainly for XQuery.
2342: */
2343:
2344: protected void setLocation(Expression exp) {
2345: if (exp instanceof ComputedExpression) {
2346: setLocation(exp, t.currentTokenStartOffset);
2347: }
2348: }
2349:
2350: /**
2351: * Set location information on an expression. At present only the line number
2352: * is retained. Needed mainly for XQuery. This version of the method supplies an
2353: * explicit offset (character position within the expression or query), which the tokenizer
2354: * can convert to a line number and column number.
2355: */
2356:
2357: protected void setLocation(Expression exp, int offset) {
2358: // TODO: we are losing the column position and retaining only the line number
2359: int line = t.getLineNumber(offset);
2360: if (exp instanceof ComputedExpression
2361: && ((ComputedExpression) exp).getLocationId() == -1) {
2362: int loc = env.getLocationMap().allocateLocationId(
2363: env.getSystemId(), line);
2364: ComputedExpression cexp = (ComputedExpression) exp;
2365: cexp.setLocationId(loc);
2366: // add a temporary container to provide location information
2367: if (cexp.getParentExpression() == null) {
2368: TemporaryContainer container = new TemporaryContainer(
2369: env.getLocationMap(), loc);
2370: cexp.setParentExpression(container);
2371: }
2372: }
2373: }
2374:
2375: /**
2376: * If tracing, wrap an instruction in a trace instruction
2377: */
2378:
2379: protected Expression makeTracer(int startOffset, Expression exp,
2380: int construct, int objectNameCode) {
2381: if (env.getConfiguration().getTraceListener() != null) {
2382: TraceExpression trace = new TraceExpression(exp);
2383: long lc = t.getLineAndColumn(startOffset);
2384: trace.setLineNumber((int) (lc >> 32));
2385: trace.setColumnNumber((int) (lc & 0x7fffffff));
2386: trace.setSystemId(env.getSystemId());
2387: trace.setNamespaceResolver(env.getNamespaceResolver());
2388: trace.setConstructType(construct);
2389: trace.setObjectNameCode(objectNameCode);
2390: return trace;
2391: } else {
2392: return exp;
2393: }
2394: }
2395:
2396: /**
2397: * Test whether the current token is a given keyword.
2398: * @param s The string to be compared with the current token
2399: * @return true if they are the same
2400: */
2401:
2402: protected boolean isKeyword(String s) {
2403: return (t.currentToken == Token.NAME && t.currentTokenValue
2404: .equals(s));
2405: }
2406:
2407: public void setScanOnly(boolean scanOnly) {
2408: this .scanOnly = scanOnly;
2409: }
2410:
2411: public static class ForClause {
2412:
2413: public RangeVariableDeclaration rangeVariable;
2414: public RangeVariableDeclaration positionVariable;
2415: public Expression sequence;
2416: public SequenceType requiredType;
2417: public int offset;
2418: }
2419:
2420: protected static class TemporaryContainer implements Container,
2421: LocationProvider, Serializable {
2422: private LocationMap map;
2423: private int locationId;
2424:
2425: public TemporaryContainer(LocationMap map, int locationId) {
2426: this .map = map;
2427: this .locationId = locationId;
2428: }
2429:
2430: public Executable getExecutable() {
2431: return null;
2432: }
2433:
2434: public LocationProvider getLocationProvider() {
2435: return map;
2436: }
2437:
2438: public String getPublicId() {
2439: return null;
2440: }
2441:
2442: public String getSystemId() {
2443: return map.getSystemId(locationId);
2444: }
2445:
2446: public int getLineNumber() {
2447: return map.getLineNumber(locationId);
2448: }
2449:
2450: public int getColumnNumber() {
2451: return -1;
2452: }
2453:
2454: public String getSystemId(int locationId) {
2455: return getSystemId();
2456: }
2457:
2458: public int getLineNumber(int locationId) {
2459: return getLineNumber();
2460: }
2461: }
2462:
2463: }
2464:
2465: //
2466: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
2467: // you may not use this file except in compliance with the License. You may obtain a copy of the
2468: // License at http://www.mozilla.org/MPL/
2469: //
2470: // Software distributed under the License is distributed on an "AS IS" basis,
2471: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
2472: // See the License for the specific language governing rights and limitations under the License.
2473: //
2474: // The Original Code is: all this file.
2475: //
2476: // The Initial Developer of the Original Code is Michael H. Kay.
2477: //
2478: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
2479: //
2480: // Contributor(s): none.
2481: //
|