KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > expr > ExpressionParser


1 package net.sf.saxon.expr;
2 import net.sf.saxon.Err;
3 import net.sf.saxon.style.StandardNames;
4 import net.sf.saxon.event.LocationProvider;
5 import net.sf.saxon.instruct.Block;
6 import net.sf.saxon.instruct.Executable;
7 import net.sf.saxon.instruct.LocationMap;
8 import net.sf.saxon.instruct.TraceExpression;
9 import net.sf.saxon.om.*;
10 import net.sf.saxon.pattern.*;
11 import net.sf.saxon.sort.Reverser;
12 import net.sf.saxon.trace.Location;
13 import net.sf.saxon.trans.DynamicError;
14 import net.sf.saxon.trans.StaticError;
15 import net.sf.saxon.trans.XPathException;
16 import net.sf.saxon.type.*;
17 import net.sf.saxon.value.*;
18
19 import java.util.ArrayList JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Stack JavaDoc;
22
23 /**
24  * Parser for XPath expressions and XSLT patterns.
25  *
26  * This code was originally inspired by James Clark's xt but has been totally rewritten (several times)
27  *
28  * @author Michael Kay
29  */

30
31
32 public class ExpressionParser {
33
34     protected Tokenizer t;
35     protected StaticContext env;
36     protected Stack JavaDoc rangeVariables = null;
37         // The stack holds a list of range variables that are in scope.
38
// Each entry on the stack is a VariableDeclaration object containing details
39
// of the variable.
40

41     protected boolean scanOnly = false;
42         // scanOnly is set to true while attributes in direct element constructors
43
// are being processed. We need to parse enclosed expressions in the attribute
44
// in order to find the end of the attribute value, but we don't yet know the
45
// full namespace context at this stage.
46

47     protected int language = XPATH; // know which language we are parsing, for diagnostics
48
protected static final int XPATH = 0;
49     protected static final int XSLT_PATTERN = 1;
50     protected static final int SEQUENCE_TYPE = 2;
51     protected static final int XQUERY = 3;
52
53     public ExpressionParser(){}
54
55     public Tokenizer getTokenizer() {
56         return t;
57     }
58
59     /**
60      * Read the next token, catching any exception thrown by the tokenizer
61      */

62
63     protected void nextToken() throws StaticError {
64         try {
65             t.next();
66         } catch (StaticError err) {
67             grumble(err.getMessage());
68         }
69     }
70
71     /**
72      * Expect a given token; fail if the current token is different. Note that this method
73      * does not read any tokens.
74      *
75      * @param token the expected token
76      * @throws net.sf.saxon.trans.StaticError if the current token is not the expected
77      * token
78      */

79
80     protected void expect(int token) throws StaticError {
81         if (t.currentToken != token)
82             grumble("expected \"" + Token.tokens[token] +
83                              "\", found " + currentTokenDisplay());
84     }
85
86     /**
87      * Report a syntax error (a static error with error code XP0003)
88      * @param message the error message
89      * @exception net.sf.saxon.trans.StaticError always thrown: an exception containing the
90      * supplied message
91      */

92
93     protected void grumble(String JavaDoc message) throws StaticError {
94         grumble(message, (language == XSLT_PATTERN ? "XTSE0340" : "XPST0003"));
95     }
96
97     /**
98      * Report a static error
99      *
100      * @param message the error message
101      * @param errorCode the error code
102      * @throws net.sf.saxon.trans.StaticError always thrown: an exception containing the
103      * supplied message
104      */

105
106     protected void grumble(String JavaDoc message, String JavaDoc errorCode) throws StaticError {
107         if (errorCode == null) {
108             errorCode = "XPST0003";
109         }
110         String JavaDoc s = t.recentText();
111         int line = t.getLineNumber();
112         int column = t.getColumnNumber();
113         String JavaDoc lineInfo = (line==1 ? "" : ("on line " + line + ' '));
114         String JavaDoc columnInfo = "at char " + column + ' ';
115         String JavaDoc prefix = getLanguage() + " syntax error " + columnInfo + lineInfo +
116                     (message.startsWith("...") ? "near" : "in") +
117                     ' ' + Err.wrap(s) + ":\n ";
118         StaticError err = new StaticError(prefix + message);
119         err.setErrorCode(errorCode);
120         throw err;
121     }
122
123     /**
124      * Output a warning message
125      */

126
127     protected void warning(String JavaDoc message) throws StaticError {
128         String JavaDoc s = t.recentText();
129         int line = t.getLineNumber();
130         String JavaDoc lineInfo = (line==1 ? "" : ("on line " + line + ' '));
131         String JavaDoc prefix = "Warning " + lineInfo +
132                     (message.startsWith("...") ? "near" : "in") +
133                 ' ' + Err.wrap(s) + ":\n ";
134         env.issueWarning(prefix + message, null);
135     }
136
137     /**
138      * Get the current language (XPath or XQuery)
139      */

140
141     protected String JavaDoc getLanguage() {
142         switch (language) {
143             case XPATH:
144                 return "XPath";
145             case XSLT_PATTERN:
146                 return "XSLT Pattern";
147             case SEQUENCE_TYPE:
148                 return "SequenceType";
149             case XQUERY:
150                 return "XQuery";
151             default:
152                 return "XPath";
153         }
154     }
155
156     /**
157      * Display the current token in an error message
158      *
159      * @return the display representation of the token
160      */

161     protected String JavaDoc currentTokenDisplay() {
162         if (t.currentToken==Token.NAME) {
163             return "name \"" + t.currentTokenValue + '\"';
164         } else if (t.currentToken==Token.UNKNOWN) {
165             return "(unknown token)";
166         } else {
167             return '\"' + Token.tokens[t.currentToken] + '\"';
168         }
169     }
170
171     /**
172      * Parse a string representing an expression
173      *
174      * @throws net.sf.saxon.trans.StaticError if the expression contains a syntax error
175      * @param expression the expression expressed as a String
176      * @param start offset within the string where parsing is to start
177      * @param terminator character to treat as terminating the expression
178      * @param lineNumber location of the start of the expression, for diagnostics
179      * @param env the static context for the expression
180      * @return an Expression object representing the result of parsing
181      */

182
183     public Expression parse(String JavaDoc expression, int start, int terminator, int lineNumber, StaticContext env) throws StaticError {
184         // System.err.println("Parse expression: " + expression);
185
this.env = env;
186         t = new Tokenizer();
187         try {
188             t.tokenize(expression, start, -1, lineNumber);
189         } catch (StaticError err) {
190             grumble(err.getMessage());
191         }
192         Expression exp = parseExpression();
193         if (t.currentToken != terminator) {
194             if (t.currentToken == Token.EOF && terminator == Token.RCURLY) {
195                 grumble("Missing curly brace after expression in attribute value template", "XTSE0350");
196             } else {
197                 grumble("Unexpected token " + currentTokenDisplay() + " beyond end of expression");
198             }
199         }
200         return exp;
201     }
202
203     /**
204      * Parse a string representing an XSLT pattern
205      *
206      * @throws net.sf.saxon.trans.StaticError if the pattern contains a syntax error
207      * @param pattern the pattern expressed as a String
208      * @param env the static context for the pattern
209      * @return a Pattern object representing the result of parsing
210      */

211
212     public Pattern parsePattern(String JavaDoc pattern, StaticContext env) throws StaticError {
213         //System.err.println("Parse pattern: " + pattern);
214
this.env = env;
215         language = XSLT_PATTERN;
216         t = new Tokenizer();
217         try {
218             t.tokenize(pattern, 0, -1, env.getLineNumber());
219         } catch (StaticError err) {
220             grumble(err.getMessage());
221         }
222         Pattern pat = parseUnionPattern();
223         if (t.currentToken != Token.EOF)
224             grumble("Unexpected token " + currentTokenDisplay() + " beyond end of pattern");
225         //pat.setStaticContext(env);
226
//System.err.println("Parsed [" + pattern + "] to " + pat.getClass() + " default prio = " + pat.getDefaultPriority());
227
return pat;
228     }
229
230     /**
231      * Parse a string representing a sequence type
232      *
233      * @param input the string, which should conform to the XPath SequenceType
234      * production
235      * @param env the static context
236      * @throws net.sf.saxon.trans.StaticError if any error is encountered
237      * @return a SequenceType object representing the type
238      */

239
240     public SequenceType parseSequenceType(String JavaDoc input, StaticContext env) throws StaticError {
241         this.env = env;
242         language = SEQUENCE_TYPE;
243         t = new Tokenizer();
244         try {
245             t.tokenize(input, 0, -1, 1);
246         } catch (StaticError err) {
247             grumble(err.getMessage());
248         }
249         SequenceType req = parseSequenceType();
250         if (t.currentToken != Token.EOF) {
251             grumble("Unexpected token " + currentTokenDisplay() + " beyond end of SequenceType");
252         }
253         return req;
254     }
255
256
257     //////////////////////////////////////////////////////////////////////////////////
258
// EXPRESSIONS //
259
//////////////////////////////////////////////////////////////////////////////////
260

261     /**
262      * Parse a top-level Expression:
263      * ExprSingle ( ',' ExprSingle )*
264      *
265      * @throws net.sf.saxon.trans.StaticError if the expression contains a syntax error
266      * @return the Expression object that results from parsing
267      */

268
269     protected Expression parseExpression() throws StaticError {
270         Expression exp = parseExprSingle();
271         while (t.currentToken == Token.COMMA) {
272             nextToken();
273             exp = Block.makeBlock(exp, parseExpression());
274             //exp = new AppendExpression(exp, Token.COMMA, parseExpression());
275
setLocation(exp);
276         }
277         return exp;
278     }
279
280     /**
281      * Parse an ExprSingle
282      *
283      * @throws net.sf.saxon.trans.StaticError if any error is encountered
284      * @return the resulting subexpression
285      */

286
287     protected Expression parseExprSingle() throws StaticError {
288         switch (t.currentToken) {
289             case Token.FOR:
290             case Token.LET: // XQuery only
291
return parseForExpression();
292             case Token.SOME:
293             case Token.EVERY:
294                 return parseQuantifiedExpression();
295             case Token.IF:
296                 return parseIfExpression();
297             case Token.TYPESWITCH:
298                 return parseTypeswitchExpression();
299             case Token.VALIDATE:
300             case Token.VALIDATE_STRICT:
301             case Token.VALIDATE_LAX:
302                 return parseValidateExpression();
303             case Token.PRAGMA:
304                 return parseExtensionExpression();
305
306             default:
307                 return parseOrExpression();
308         }
309     }
310
311     /**
312      * Parse a Typeswitch Expression.
313      * This construct is XQuery-only, so the XPath version of this
314      * method throws an error unconditionally
315      */

316
317     protected Expression parseTypeswitchExpression() throws StaticError {
318         grumble("typeswitch is not allowed in XPath");
319         return null;
320     }
321
322     /**
323      * Parse a Validate Expression.
324      * This construct is XQuery-only, so the XPath version of this
325      * method throws an error unconditionally
326      */

327
328     protected Expression parseValidateExpression() throws StaticError {
329         grumble("validate{} expressions are not allowed in XPath");
330         return null;
331     }
332
333     /**
334      * Parse an Extension Expression
335      * This construct is XQuery-only, so the XPath version of this
336      * method throws an error unconditionally
337      */

338
339     protected Expression parseExtensionExpression() throws StaticError {
340         grumble("extension expressions (#...#) are not allowed in XPath");
341         return null;
342     }
343
344     /**
345      * Parse an OrExpression:
346      * AndExpr ( 'or' AndExpr )*
347      *
348      * @throws net.sf.saxon.trans.StaticError if any error is encountered
349      * @return the resulting subexpression
350      */

351
352     private Expression parseOrExpression() throws StaticError {
353         Expression exp = parseAndExpression();
354         while (t.currentToken == Token.OR) {
355             nextToken();
356             exp = new BooleanExpression(exp, Token.OR, parseAndExpression());
357             setLocation(exp);
358         }
359         return exp;
360     }
361
362     /**
363      * Parse an AndExpr:
364      * EqualityExpr ( 'and' EqualityExpr )*
365      *
366      * @throws net.sf.saxon.trans.StaticError if any error is encountered
367      * @return the resulting subexpression
368      */

369
370     private Expression parseAndExpression() throws StaticError {
371         Expression exp = parseComparisonExpression();
372         while (t.currentToken == Token.AND) {
373             nextToken();
374             exp = new BooleanExpression(exp, Token.AND, parseComparisonExpression());
375             setLocation(exp);
376         }
377         return exp;
378     }
379
380     /**
381      * Parse a FOR expression:
382      * for $x in expr (',' $y in expr)* 'return' expr
383      *
384      * @throws net.sf.saxon.trans.StaticError if any error is encountered
385      * @return the resulting subexpression
386      */

387
388     protected Expression parseForExpression() throws StaticError {
389         if (t.currentToken==Token.LET) {
390             grumble("'let' is not supported in XPath");
391         }
392         return parseMappingExpression();
393     }
394
395     /**
396      * Parse a quantified expression:
397      * (some|every) $x in expr 'satisfies' expr
398      *
399      * @throws net.sf.saxon.trans.StaticError if any error is encountered
400      * @return the resulting subexpression
401      */

402
403     private Expression parseQuantifiedExpression() throws StaticError {
404         return parseMappingExpression();
405     }
406
407     /**
408      * Parse a mapping expression. This is a common routine that handles
409      * XPath for expressions and quantified expressions.
410      *
411      * <p>Syntax: <br/>
412      * (for|some|every) $x in expr (',' $y in expr)* (return|satisfies) expr
413      * </p>
414      *
415      * <p>On entry, the current token indicates whether a for, some, or every
416      * expression is expected.</p>
417      *
418      * @throws net.sf.saxon.trans.StaticError if any error is encountered
419      * @return the resulting subexpression
420      */

421
422     protected Expression parseMappingExpression() throws StaticError {
423         int offset = t.currentTokenStartOffset;
424         int operator = t.currentToken;
425         List JavaDoc clauseList = new ArrayList JavaDoc(3);
426         do {
427             ForClause clause = new ForClause();
428             clause.offset = offset;
429             clause.requiredType = SequenceType.SINGLE_ITEM;
430             clauseList.add(clause);
431             nextToken();
432             expect(Token.DOLLAR);
433             nextToken();
434             expect(Token.NAME);
435             String JavaDoc var = t.currentTokenValue;
436
437             // declare the range variable
438
RangeVariableDeclaration v = new RangeVariableDeclaration();
439             v.setNameCode(makeNameCode(var, false));
440             v.setRequiredType(SequenceType.SINGLE_ITEM);
441             v.setVariableName(var);
442             clause.rangeVariable = v;
443             nextToken();
444
445             if (isKeyword("as") && "XQuery".equals(getLanguage())) {
446                 nextToken();
447                 SequenceType type = parseSequenceType();
448                 clause.requiredType = type;
449                 v.setRequiredType(type);
450                 if (type.getCardinality() != StaticProperty.EXACTLY_ONE) {
451                     grumble("Cardinality of range variable must be exactly one");
452                 }
453             }
454
455             // "at" clauses are not recognized in XPath
456
clause.positionVariable = null;
457
458             // process the "in" clause
459
expect(Token.IN);
460             nextToken();
461             clause.sequence = parseExprSingle();
462             declareRangeVariable(clause.rangeVariable);
463
464         } while (t.currentToken==Token.COMMA);
465
466         // process the "return/satisfies" expression (called the "action")
467
if (operator==Token.FOR) {
468             expect(Token.RETURN);
469         } else {
470             expect(Token.SATISFIES);
471         }
472         nextToken();
473         Expression action = parseExprSingle();
474
475         // work back through the list of range variables, fixing up all references
476
// to the variables in the inner expression
477

478         for (int i = clauseList.size()-1; i>=0; i--) {
479             ForClause fc = (ForClause)clauseList.get(i);
480             Assignation exp;
481             if (operator==Token.FOR) {
482                 exp = new ForExpression();
483             } else {
484                 exp = new QuantifiedExpression();
485                 ((QuantifiedExpression)exp).setOperator(operator);
486             }
487             setLocation(exp, offset);
488             exp.setVariableDeclaration(fc.rangeVariable);
489             exp.setSequence(fc.sequence);
490
491             // Attempt to give the range variable a more precise type, base on analysis of the
492
// "action" expression. This will often be approximate, because variables and function
493
// calls in the action expression have not yet been resolved. We rely on the ability
494
// of all expressions to return some kind of type information even if this is
495
// imprecise.
496

497             if (fc.requiredType == SequenceType.SINGLE_ITEM) {
498                 SequenceType type = SequenceType.makeSequenceType(
499                         fc.sequence.getItemType(), StaticProperty.EXACTLY_ONE);
500                 fc.rangeVariable.setRequiredType(type);
501             } else {
502                 fc.rangeVariable.setRequiredType(fc.requiredType);
503             }
504             exp.setAction(action);
505
506             // for the next outermost "for" clause, the "action" is this ForExpression
507
action = exp;
508         }
509
510         // undeclare all the range variables
511

512         for (int i = clauseList.size()-1; i>=0; i--) {
513             undeclareRangeVariable();
514         }
515         //action = makeTracer(offset, action, Location.FOR_EXPRESSION, -1);
516
return action;
517     }
518
519
520     /**
521      * Parse an IF expression:
522      * if '(' expr ')' 'then' expr 'else' expr
523      *
524      * @throws net.sf.saxon.trans.StaticError if any error is encountered
525      * @return the resulting subexpression
526      */

527
528     private Expression parseIfExpression() throws StaticError {
529         // left paren already read
530
int ifoffset = t.currentTokenStartOffset;
531         nextToken();
532         Expression condition = parseExpression();
533         expect(Token.RPAR);
534         nextToken();
535         int thenoffset = t.currentTokenStartOffset;
536         expect(Token.THEN);
537         nextToken();
538         Expression thenExp = makeTracer(thenoffset, parseExpression(), Location.THEN_EXPRESSION, -1);
539         int elseoffset = t.currentTokenStartOffset;
540         expect(Token.ELSE);
541         nextToken();
542         Expression elseExp = makeTracer(elseoffset, parseExprSingle(), Location.ELSE_EXPRESSION, -1);
543         Expression ifExp = new IfExpression(condition, thenExp, elseExp);
544         setLocation(ifExp, ifoffset);
545         return makeTracer(ifoffset, ifExp, Location.IF_EXPRESSION, -1);
546     }
547
548     /**
549      * Parse an "instance of" expression
550      * Expr ("instance" "of") SequenceType
551      *
552      * @throws net.sf.saxon.trans.StaticError if any error is encountered
553      * @return the resulting subexpression
554      */

555
556     private Expression parseInstanceOfExpression() throws StaticError {
557         Expression exp = parseTreatExpression();
558         if (t.currentToken == Token.INSTANCE_OF) {
559             nextToken();
560             exp = new InstanceOfExpression(exp, parseSequenceType());
561             setLocation(exp);
562         }
563         return exp;
564     }
565
566     /**
567      * Parse a "treat as" expression
568      * castable-expression ("treat" "as" SequenceType )?
569      *
570      * @throws net.sf.saxon.trans.StaticError if any error is encountered
571      * @return the resulting subexpression
572      */

573
574     private Expression parseTreatExpression() throws StaticError {
575         Expression exp = parseCastableExpression();
576         if (t.currentToken == Token.TREAT_AS) {
577             nextToken();
578             SequenceType target = parseSequenceType();
579             exp = TreatExpression.make(exp, target);
580             setLocation(exp);
581         }
582         return exp;
583     }
584
585     /**
586      * Parse a "castable as" expression
587      * Expr "castable as" AtomicType "?"?
588      *
589      * @throws net.sf.saxon.trans.StaticError if any error is encountered
590      * @return the resulting subexpression
591      */

592
593     private Expression parseCastableExpression() throws StaticError {
594         Expression exp = parseCastExpression();
595         if (t.currentToken == Token.CASTABLE_AS) {
596             nextToken();
597             expect(Token.NAME);
598             AtomicType at = getAtomicType(t.currentTokenValue);
599             if (at.getFingerprint() == StandardNames.XDT_ANY_ATOMIC_TYPE) {
600                 grumble("No value is castable to xdt:anyAtomicType", "XPST0080");
601             }
602             if (at.getFingerprint() == StandardNames.XS_NOTATION) {
603                 grumble("No value is castable to xs:NOTATION", "XPST0080");
604             }
605             nextToken();
606             boolean allowEmpty = (t.currentToken == Token.QMARK);
607             if (allowEmpty) {
608                 nextToken();
609             }
610             exp = new CastableExpression(exp, at, allowEmpty);
611             setLocation(exp);
612         }
613         return exp;
614     }
615
616     /**
617      * Parse a "cast as" expression
618      * castable-expression ("cast" "as" AtomicType "?"?)
619      *
620      * @throws net.sf.saxon.trans.StaticError if any error is encountered
621      * @return the resulting subexpression
622      */

623
624     private Expression parseCastExpression() throws StaticError {
625         Expression exp = parseUnaryExpression();
626         if (t.currentToken == Token.CAST_AS) {
627             nextToken();
628             expect(Token.NAME);
629             AtomicType at = getAtomicType(t.currentTokenValue);
630             if (at.getFingerprint() == StandardNames.XDT_ANY_ATOMIC_TYPE) {
631                 grumble("Cannot cast to xdt:anyAtomicType", "XPST0080");
632             }
633             if (at.getFingerprint() == StandardNames.XS_NOTATION) {
634                 grumble("Cannot cast to xs:NOTATION", "XPST0080");
635             }
636             nextToken();
637             boolean allowEmpty = (t.currentToken == Token.QMARK);
638             if (allowEmpty) {
639                 nextToken();
640             }
641             exp = new CastExpression(exp, at, allowEmpty);
642             // A QName or NOTATION constructor function must be evaluated now, while we know the namespace context
643
if (((AtomicType)exp.getItemType()).isNamespaceSensitive()) {
644                 try {
645                     return ((CastExpression)exp).doQNameCast(env);
646                 } catch (XPathException e) {
647                     grumble(e.getMessage());
648                 }
649             }
650             setLocation(exp);
651         }
652         return exp;
653     }
654
655     /**
656      * Analyze a token whose expected value is the name of an atomic type,
657      * and return the object representing the atomic type.
658      * @param qname The lexical QName of the atomic type
659      * @return The atomic type
660      * @throws net.sf.saxon.trans.StaticError if the QName is invalid or if no atomic type of that
661      * name exists as a built-in type or a type in an imported schema
662      */

663     private AtomicType getAtomicType(String JavaDoc qname) throws StaticError {
664         if (scanOnly) {
665             return Type.STRING_TYPE;
666         }
667         try {
668             String JavaDoc[] parts = Name.getQNameParts(qname);
669             String JavaDoc uri;
670             if ("".equals(parts[0])) {
671                 short uriCode = env.getDefaultElementNamespace();
672                 uri = env.getNamePool().getURIFromURICode(uriCode);
673             } else {
674                 try {
675                     uri = env.getURIForPrefix(parts[0]);
676                 } catch (XPathException err) {
677                     grumble(err.getMessage());
678                     uri = "";
679                 }
680             }
681
682             boolean builtInNamespace = uri.equals(NamespaceConstant.SCHEMA);
683             if (!builtInNamespace && NamespaceConstant.isXDTNamespace(uri)) {
684                 uri = NamespaceConstant.XDT;
685                 builtInNamespace = true;
686             }
687
688             if (builtInNamespace) {
689                 ItemType t = Type.getBuiltInItemType(uri, parts[1]);
690                 if (t == null) {
691                     grumble("Unknown atomic type " + qname, "XPST0051");
692                 }
693                 if (t instanceof BuiltInAtomicType) {
694                     // test removed because it's done later in CastExpression.simplify - where it works
695
// for constructor functions as well.
696
// if (!env.isAllowedBuiltInType((BuiltInAtomicType)t)) {
697
// warning("The type " + qname + " is not recognized by a Basic XSLT Processor. " +
698
// "Saxon permits it for the time being.");
699
// }
700
return (AtomicType)t;
701                 } else {
702                     grumble("The type " + qname + " is not atomic");
703                 }
704             } else if (uri.equals(NamespaceConstant.JAVA_TYPE)) {
705                 Class JavaDoc theClass = null;
706                 try {
707                     String JavaDoc className = parts[1].replace('-', '$');
708                     theClass = env.getConfiguration().getClass(className, false, null);
709                 } catch (XPathException err) {
710                     grumble("Unknown Java class " + parts[1]);
711                 }
712                 return new ExternalObjectType(theClass);
713             } else {
714                 if (env.isImportedSchema(uri)) {
715                     int fp = env.getNamePool().getFingerprint(uri, parts[1]);
716                     if (fp == -1) {
717                         grumble("Unknown type " + qname);
718                     }
719                     SchemaType st = env.getConfiguration().getSchemaType(fp);
720                     if (st == null) {
721                         grumble("Unknown atomic type " + qname);
722                     } else if (st instanceof AtomicType) {
723                         return (AtomicType)st;
724                     } else if (st.isComplexType()) {
725                         grumble("Type (" + qname + ") is a complex type");
726                         return null;
727                     } else {
728                         grumble("Type (" + qname + ") is a list or union type");
729                         return null;
730                     }
731
732                 } else {
733                     if ("".equals(uri)) {
734                         grumble("There is no imported schema for the null namespace");
735                     } else {
736                         grumble("There is no imported schema for namespace " + uri);
737                     }
738                     return null;
739                 }
740             }
741             grumble("Unknown atomic type " + qname);
742         } catch (QNameException err) {
743             grumble(err.getMessage());
744         }
745         return null;
746     }
747     /**
748      * Parse a ComparisonExpr:<br>
749      * RangeExpr ( op RangeExpr )*
750      * where op is one of =, <, >, !=, <=, >=,
751      * eq, lt, gt, ne, le, ge,
752      * is, isnot, <<, >>
753      *
754      * @throws net.sf.saxon.trans.StaticError if any error is encountered
755      * @return the resulting subexpression
756      */

757
758     private Expression parseComparisonExpression() throws StaticError {
759         Expression exp = parseRangeExpression();
760         switch (t.currentToken) {
761             case Token.IS:
762             case Token.PRECEDES:
763             case Token.FOLLOWS:
764                 int op = t.currentToken;
765                 nextToken();
766                 exp = new IdentityComparison(exp, op, parseRangeExpression());
767                 setLocation(exp);
768                 return exp;
769
770             case Token.EQUALS:
771             case Token.NE:
772             case Token.LT:
773             case Token.GT:
774             case Token.LE:
775             case Token.GE:
776                 op = t.currentToken;
777                 nextToken();
778                 exp = env.getConfiguration().getOptimizer().makeGeneralComparison(
779                         exp, op, parseRangeExpression(), env.isInBackwardsCompatibleMode());
780                 setLocation(exp);
781                 return exp;
782
783             case Token.FEQ:
784             case Token.FNE:
785             case Token.FLT:
786             case Token.FGT:
787             case Token.FLE:
788             case Token.FGE:
789                 op = t.currentToken;
790                 nextToken();
791                 exp = new ValueComparison(exp, op, parseRangeExpression());
792                 setLocation(exp);
793                 return exp;
794
795             default:
796                 return exp;
797         }
798     }
799
800     /**
801      * Parse a RangeExpr:<br>
802      * AdditiveExpr ('to' AdditiveExpr )?
803      *
804      * @throws net.sf.saxon.trans.StaticError if any error is encountered
805      * @return the resulting subexpression
806      */

807
808     private Expression parseRangeExpression() throws StaticError {
809         Expression exp = parseAdditiveExpression();
810         if (t.currentToken == Token.TO ) {
811             nextToken();
812             exp = new RangeExpression(exp, Token.TO, parseAdditiveExpression());
813             setLocation(exp);
814         }
815         return exp;
816     }
817
818
819
820     /**
821      * Parse the sequence type production.
822      * Provisionally, we use the syntax (QName | node-kind "()") ( "*" | "+" | "?" )?
823      * We also allow "element of type QName" and "attribute of type QName"
824      * The QName must be the name of a built-in schema-defined data type.
825      *
826      * @throws net.sf.saxon.trans.StaticError if any error is encountered
827      * @return the resulting subexpression
828      */

829
830     protected SequenceType parseSequenceType() throws StaticError {
831         ItemType primaryType;
832         if (t.currentToken == Token.NAME) {
833             primaryType = getAtomicType(t.currentTokenValue);
834             nextToken();
835         } else if (t.currentToken == Token.NODEKIND) {
836             if (t.currentTokenValue == "item") {
837                 nextToken();
838                 expect(Token.RPAR);
839                 nextToken();
840                 primaryType = AnyItemType.getInstance();
841             } else if (t.currentTokenValue == "void" || t.currentTokenValue == "empty-sequence") {
842                     // TODO: the "void" keyword is obsolete
843
nextToken();
844                 expect(Token.RPAR);
845                 nextToken();
846                 return SequenceType.makeSequenceType(NoNodeTest.getInstance(), StaticProperty.EMPTY);
847                 // return before trying to read an occurrence indicator
848
} else {
849                 primaryType = parseKindTest();
850             }
851         } else {
852             grumble("Expected type name in SequenceType, found " + Token.tokens[t.currentToken]);
853             return null;
854         }
855
856         int occurrenceFlag;
857         switch (t.currentToken) {
858             case Token.STAR:
859             case Token.MULT:
860                 // "*" will be tokenized different ways depending on what precedes it
861
occurrenceFlag = StaticProperty.ALLOWS_ZERO_OR_MORE;
862                 // Make the tokenizer ignore the occurrence indicator when classifying the next token
863
t.currentToken = Token.RPAR;
864                 nextToken();
865                 break;
866             case Token.PLUS:
867                 occurrenceFlag = StaticProperty.ALLOWS_ONE_OR_MORE;
868                 // Make the tokenizer ignore the occurrence indicator when classifying the next token
869
t.currentToken = Token.RPAR;
870                 nextToken();
871                 break;
872             case Token.QMARK:
873                 occurrenceFlag = StaticProperty.ALLOWS_ZERO_OR_ONE;
874                 // Make the tokenizer ignore the occurrence indicator when classifying the next token
875
t.currentToken = Token.RPAR;
876                 nextToken();
877                 break;
878             default:
879                 occurrenceFlag = StaticProperty.EXACTLY_ONE;
880         }
881         return SequenceType.makeSequenceType(primaryType, occurrenceFlag);
882     }
883
884
885     /**
886      * Parse an AdditiveExpr:
887      * MultiplicativeExpr ( (+|-) MultiplicativeExpr )*
888      *
889      * @throws net.sf.saxon.trans.StaticError if any error is encountered
890      * @return the resulting subexpression
891      */

892
893     private Expression parseAdditiveExpression() throws StaticError {
894         Expression exp = parseMultiplicativeExpression();
895         while (t.currentToken == Token.PLUS ||
896                 t.currentToken == Token.MINUS ) {
897             int op = t.currentToken;
898             nextToken();
899             exp = new ArithmeticExpression(exp, op, parseMultiplicativeExpression());
900             setLocation(exp);
901         }
902         return exp;
903     }
904
905     /**
906      * Parse a MultiplicativeExpr:<br>
907      * UnionExpr ( (*|div|idiv|mod) UnionExpr )*
908      *
909      * @throws net.sf.saxon.trans.StaticError if any error is encountered
910      * @return the resulting subexpression
911      */

912
913     private Expression parseMultiplicativeExpression() throws StaticError {
914         Expression exp = parseUnionExpression();
915         while (t.currentToken == Token.MULT ||
916                 t.currentToken == Token.DIV ||
917                 t.currentToken == Token.IDIV ||
918                 t.currentToken == Token.MOD ) {
919             int op = t.currentToken;
920             nextToken();
921             exp = new ArithmeticExpression(exp, op, parseUnionExpression());
922             setLocation(exp);
923         }
924         return exp;
925     }
926
927     /**
928      * Parse a UnaryExpr:<br>
929      * ('+'|'-')* ValueExpr
930      * parsed as ('+'|'-')? UnaryExpr
931      *
932      * @throws net.sf.saxon.trans.StaticError if any error is encountered
933      * @return the resulting subexpression
934      */

935
936     private Expression parseUnaryExpression() throws StaticError {
937         Expression exp;
938         switch (t.currentToken) {
939         case Token.MINUS:
940             nextToken();
941             exp = new ArithmeticExpression(new IntegerValue(0),
942                                           Token.NEGATE,
943                                           parseUnaryExpression());
944             break;
945         case Token.PLUS:
946             nextToken();
947             // Unary plus: can't ignore it completely, it might be a type error, or it might
948
// force conversion to a number which would affect operations such as "=".
949
exp = new ArithmeticExpression(new IntegerValue(0),
950                                           Token.PLUS,
951                                           parseUnaryExpression());
952             break;
953         case Token.VALIDATE:
954         case Token.VALIDATE_STRICT:
955         case Token.VALIDATE_LAX:
956             exp = parseValidateExpression();
957             break;
958         case Token.PRAGMA:
959             exp = parseExtensionExpression();
960             break;
961         default:
962             exp = parsePathExpression();
963         }
964         setLocation(exp);
965         return exp;
966     }
967
968     /**
969      * Parse a UnionExpr:<br>
970      * IntersectExceptExpr ( "|" | "union" IntersectExceptExpr )*
971      *
972      * @throws net.sf.saxon.trans.StaticError if any error is encountered
973      * @return the resulting subexpression
974      */

975
976     private Expression parseUnionExpression() throws StaticError {
977         Expression exp = parseIntersectExpression();
978         while (t.currentToken == Token.UNION ) {
979             nextToken();
980             exp = new VennExpression(exp, Token.UNION, parseIntersectExpression());
981             setLocation(exp);
982         }
983         return exp;
984     }
985
986     /**
987      * Parse an IntersectExceptExpr:<br>
988      * PathExpr ( ( 'intersect' | 'except') PathExpr )*
989      *
990      * @throws net.sf.saxon.trans.StaticError if any error is encountered
991      * @return the resulting subexpression
992      */

993
994     private Expression parseIntersectExpression() throws StaticError {
995         Expression exp = parseInstanceOfExpression();
996         while (t.currentToken == Token.INTERSECT ||
997                 t.currentToken == Token.EXCEPT ) {
998             int op = t.currentToken;
999             nextToken();
1000            exp = new VennExpression(exp, op, parseInstanceOfExpression());
1001            setLocation(exp);
1002        }
1003        return exp;
1004    }
1005
1006
1007
1008    /**
1009     * Test whether the current token is one that can start a RelativePathExpression
1010     *
1011     * @return the resulting subexpression
1012     */

1013
1014    private boolean atStartOfRelativePath() {
1015        switch(t.currentToken) {
1016            case Token.AXIS:
1017            case Token.AT:
1018            case Token.NAME:
1019            case Token.PREFIX:
1020            case Token.SUFFIX:
1021            case Token.STAR:
1022            case Token.NODEKIND:
1023            case Token.DOT:
1024            case Token.DOTDOT:
1025            case Token.FUNCTION:
1026            case Token.STRING_LITERAL:
1027            case Token.NUMBER:
1028            case Token.LPAR:
1029                return true;
1030            default:
1031                return false;
1032        }
1033    }
1034
1035    /**
1036     * Parse a PathExpresssion. This includes "true" path expressions such as A/B/C, and also
1037     * constructs that may start a path expression such as a variable reference $name or a
1038     * parenthesed expression (A|B). Numeric and string literals also come under this heading.
1039     *
1040     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1041     * @return the resulting subexpression
1042     */

1043
1044    private Expression parsePathExpression() throws StaticError {
1045        switch (t.currentToken) {
1046        case Token.SLASH:
1047            nextToken();
1048            final RootExpression start = new RootExpression();
1049            setLocation(start);
1050            if (atStartOfRelativePath()) {
1051                //final Expression path = new PathExpression(start, parseRelativePath(null));
1052
final Expression path = parseRemainingPath(start);
1053                setLocation(path);
1054                return path;
1055            } else {
1056                return start;
1057            }
1058
1059        case Token.SLSL:
1060            // The logic for absolute path expressions changed in 8.4 so that //A/B/C parses to
1061
// (((root()/descendant-or-self::node())/A)/B)/C rather than
1062
// (root()/descendant-or-self::node())/(((A)/B)/C) as previously. This is to allow
1063
// the subsequent //A optimization to kick in.
1064
nextToken();
1065            // add in the implicit descendant-or-self::node() step
1066
// final RootExpression start2 = new RootExpression();
1067
// setLocation(start2);
1068
// final AxisExpression axisExp = new AxisExpression(Axis.DESCENDANT_OR_SELF, null);
1069
// setLocation(axisExp);
1070
// final PathExpression pathExp = new PathExpression(axisExp, parseRelativePath(null));
1071
// setLocation(pathExp);
1072
// final Expression exp = new PathExpression(start2, pathExp);
1073
// setLocation(exp);
1074
// return exp;
1075
final RootExpression start2 = new RootExpression();
1076            setLocation(start2);
1077            final AxisExpression axisExp = new AxisExpression(Axis.DESCENDANT_OR_SELF, null);
1078            setLocation(axisExp);
1079            final Expression exp = parseRemainingPath(new PathExpression(start2, axisExp));
1080            setLocation(exp);
1081            return exp;
1082        default:
1083            return parseRelativePath();
1084        }
1085
1086    }
1087
1088
1089    /**
1090     * Parse a relative path (a sequence of steps). Called when the current token immediately
1091     * follows a separator (/ or //), or an implicit separator (XYZ is equivalent to ./XYZ)
1092     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1093     * @return the resulting subexpression
1094     */

1095
1096    protected Expression parseRelativePath() throws StaticError {
1097        Expression exp = parseStepExpression();
1098        while (t.currentToken == Token.SLASH ||
1099                t.currentToken == Token.SLSL ) {
1100            int op = t.currentToken;
1101            nextToken();
1102            Expression next = parseStepExpression();
1103            if (op == Token.SLASH) {
1104                exp = new PathExpression(exp, next);
1105            } else {
1106                // add implicit descendant-or-self::node() step
1107
exp = new PathExpression(exp,
1108                        new PathExpression(new AxisExpression(Axis.DESCENDANT_OR_SELF, null),
1109                            next));
1110            }
1111            setLocation(exp);
1112        }
1113        return exp;
1114    }
1115
1116    /**
1117     * Parse the remaining steps of an absolute path expression (one starting in "/" or "//"). Note that the
1118     * token immediately after the "/" or "//" has already been read, and in the case of "/", it has been confirmed
1119     * that we have a path expression starting with "/" rather than a standalone "/" expression.
1120     * @param start the initial implicit expression: root() in the case of "/", root()/descendant-or-self::node in
1121     * the case of "//"
1122     * @return the completed path expression
1123     * @throws StaticError
1124     */

1125    protected Expression parseRemainingPath(Expression start) throws StaticError {
1126            Expression exp = start;
1127            int op = Token.SLASH;
1128            while (true) {
1129                Expression next = parseStepExpression();
1130                if (op == Token.SLASH) {
1131                    exp = new PathExpression(exp, next);
1132                } else {
1133                    // add implicit descendant-or-self::node() step
1134
exp = new PathExpression(exp,
1135                            new PathExpression(new AxisExpression(Axis.DESCENDANT_OR_SELF, null),
1136                                next));
1137                }
1138                setLocation(exp);
1139                op = t.currentToken;
1140                if (op != Token.SLASH && op != Token.SLSL) {
1141                    break;
1142                }
1143                nextToken();
1144            }
1145            return exp;
1146        }
1147
1148
1149    /**
1150     * Parse a step (including an optional sequence of predicates)
1151     *
1152     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1153     * @return the resulting subexpression
1154     */

1155
1156    protected Expression parseStepExpression() throws StaticError {
1157        Expression step = parseBasicStep();
1158
1159        // When the filter is applied to an Axis step, the nodes are considered in
1160
// axis order. In all other cases they are considered in document order
1161
boolean reverse = (step instanceof AxisExpression) &&
1162                          Axis.isReverse[((AxisExpression)step).getAxis()];
1163
1164        while (t.currentToken == Token.LSQB) {
1165            nextToken();
1166            Expression predicate = parseExpression();
1167            expect(Token.RSQB);
1168            nextToken();
1169            step = new FilterExpression(step, predicate, env);
1170            setLocation(step);
1171        }
1172        if (reverse) {
1173            return new Reverser(step);
1174        } else {
1175            return step;
1176        }
1177    }
1178
1179    /**
1180     * Parse a basic step expression (without the predicates)
1181     *
1182     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1183     * @return the resulting subexpression
1184     */

1185
1186    private Expression parseBasicStep() throws StaticError {
1187        switch(t.currentToken) {
1188        case Token.DOLLAR:
1189            nextToken();
1190            expect(Token.NAME);
1191            String JavaDoc var = t.currentTokenValue;
1192            nextToken();
1193
1194            if (scanOnly) {
1195                return new ContextItemExpression();
1196                // don't do any semantic checks during a prescan
1197
}
1198
1199            int vtest = makeNameCode(var, false) & 0xfffff;
1200
1201            // See if it's a range variable or a variable in the context
1202
VariableDeclaration b = findRangeVariable(vtest);
1203            VariableReference ref;
1204            if (b!=null) {
1205                ref = new VariableReference(b);
1206            } else {
1207                try {
1208                    ref = new VariableReference(env.bindVariable(vtest));
1209                } catch (XPathException err) {
1210                    grumble("Variable $" + var + " has not been declared");
1211                    ref = null;
1212                }
1213            }
1214            setLocation(ref);
1215            return ref;
1216
1217        case Token.LPAR:
1218            nextToken();
1219            if (t.currentToken==Token.RPAR) {
1220                nextToken();
1221                return EmptySequence.getInstance();
1222            }
1223            Expression seq = parseExpression();
1224            expect(Token.RPAR);
1225            nextToken();
1226            return seq;
1227
1228        case Token.STRING_LITERAL:
1229            StringValue literal = makeStringLiteral(t.currentTokenValue);
1230            nextToken();
1231            return literal;
1232
1233        case Token.NUMBER:
1234            NumericValue number = NumericValue.parseNumber(t.currentTokenValue);
1235            if (number.isNaN()) {
1236                grumble("Invalid numeric literal " + Err.wrap(t.currentTokenValue, Err.VALUE));
1237            }
1238            nextToken();
1239            return number;
1240
1241        case Token.FUNCTION:
1242            return parseFunctionCall();
1243
1244        case Token.DOT:
1245            nextToken();
1246            Expression cie = new ContextItemExpression();
1247            setLocation(cie);
1248            return cie;
1249
1250        case Token.DOTDOT:
1251            nextToken();
1252            Expression pne = new ParentNodeExpression();
1253            setLocation(pne);
1254            return pne;
1255
1256        case Token.NAME:
1257        case Token.PREFIX:
1258        case Token.SUFFIX:
1259        case Token.STAR:
1260        case Token.NODEKIND:
1261            byte defaultAxis = Axis.CHILD;
1262            if (t.currentToken == Token.NODEKIND && t.currentTokenValue == "attribute") {
1263                defaultAxis = Axis.ATTRIBUTE;
1264            }
1265            AxisExpression ae = new AxisExpression(defaultAxis, parseNodeTest(Type.ELEMENT));
1266            setLocation(ae);
1267            return ae;
1268
1269        case Token.AT:
1270            nextToken();
1271            switch(t.currentToken) {
1272
1273            case Token.NAME:
1274            case Token.PREFIX:
1275            case Token.SUFFIX:
1276            case Token.STAR:
1277            case Token.NODEKIND:
1278                AxisExpression ae2 = new AxisExpression(Axis.ATTRIBUTE, parseNodeTest(Type.ATTRIBUTE));
1279                setLocation(ae2);
1280                return ae2;
1281
1282            default:
1283                grumble("@ must be followed by a NodeTest");
1284            }
1285            break;
1286
1287        case Token.AXIS:
1288                byte axis;
1289                try {
1290                    axis = Axis.getAxisNumber(t.currentTokenValue);
1291                } catch (StaticError err) {
1292                    grumble(err.getMessage());
1293                    axis = Axis.CHILD; // error recovery
1294
}
1295                if (axis == Axis.NAMESPACE && language == XQUERY) {
1296                grumble("The namespace axis is not available in XQuery");
1297            }
1298            short principalNodeType = Axis.principalNodeType[axis];
1299            nextToken();
1300            switch (t.currentToken) {
1301
1302            case Token.NAME:
1303            case Token.PREFIX:
1304            case Token.SUFFIX:
1305            case Token.STAR:
1306            case Token.NODEKIND:
1307                Expression ax = new AxisExpression(axis, parseNodeTest(principalNodeType));
1308                setLocation(ax);
1309                return ax;
1310
1311            default:
1312                grumble("Unexpected token " + currentTokenDisplay() + " after axis name");
1313            }
1314            break;
1315
1316        case Token.KEYWORD_CURLY:
1317        case Token.ELEMENT_QNAME:
1318        case Token.ATTRIBUTE_QNAME:
1319        case Token.PI_QNAME:
1320        case Token.TAG:
1321            return parseConstructor();
1322
1323        default:
1324            grumble("Unexpected token " + currentTokenDisplay() + " in path expression");
1325            //break;
1326
}
1327        return null;
1328    }
1329
1330    /**
1331     * Method to make a string literal from a token identified as a string
1332     * literal. This is trivial in XPath, but in XQuery the method is overridden
1333     * to identify pseudo-XML character and entity references. Note that the job of handling
1334     * doubled string delimiters is done by the tokenizer.
1335     * @param currentTokenValue
1336     * @return The string value of the string literal
1337     */

1338
1339    protected StringValue makeStringLiteral(String JavaDoc currentTokenValue) throws StaticError {
1340        return StringValue.makeStringValue(currentTokenValue);
1341    }
1342
1343    /**
1344     * Parse a node constructor. This is allowed only in XQuery, so the method throws
1345     * an error for XPath.
1346     */

1347
1348    protected Expression parseConstructor() throws StaticError {
1349        grumble("Node constructor expressions are allowed only in XQuery, not in XPath");
1350        return null;
1351    }
1352
1353    /**
1354     * Parse a NodeTest.
1355     * One of QName, prefix:*, *:suffix, *, text(), node(), comment(), or
1356     * processing-instruction(literal?), or element(~,~), attribute(~,~), etc.
1357     *
1358     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1359     * @param nodeType the node type being sought if one is specified
1360     * @return the resulting NodeTest object
1361     */

1362
1363    protected NodeTest parseNodeTest(short nodeType) throws StaticError {
1364        int tok = t.currentToken;
1365        String JavaDoc tokv = t.currentTokenValue;
1366        switch (tok) {
1367        case Token.NAME:
1368            nextToken();
1369            return makeNameTest(nodeType, tokv, nodeType==Type.ELEMENT);
1370
1371        case Token.PREFIX:
1372            nextToken();
1373            return makeNamespaceTest(nodeType, tokv);
1374
1375        case Token.SUFFIX:
1376            nextToken();
1377            tokv = t.currentTokenValue;
1378            expect(Token.NAME);
1379            nextToken();
1380            return makeLocalNameTest(nodeType, tokv);
1381
1382        case Token.STAR:
1383            nextToken();
1384            return NodeKindTest.makeNodeKindTest(nodeType);
1385
1386        case Token.NODEKIND:
1387            return parseKindTest();
1388
1389        default:
1390            grumble("Unrecognized node test");
1391            return null;
1392        }
1393    }
1394
1395    /**
1396     * Parse a KindTest
1397     */

1398
1399    private NodeTest parseKindTest() throws StaticError {
1400        String JavaDoc typeName = t.currentTokenValue;
1401        boolean schemaDeclaration = (typeName.startsWith("schema-"));
1402        int primaryType = getSystemType(typeName);
1403        int nameCode = -1;
1404        int contentType;
1405        boolean empty = false;
1406        nextToken();
1407        if (t.currentToken == Token.RPAR) {
1408            if (schemaDeclaration) {
1409                grumble("schema-element() and schema-attribute() require a name to be supplied");
1410                return null;
1411            }
1412            empty = true;
1413            nextToken();
1414        }
1415        switch (primaryType) {
1416            case Type.ITEM:
1417                grumble("item() is not allowed in a path expression");
1418                return null;
1419            case Type.NODE:
1420                if (empty) {
1421                    return AnyNodeTest.getInstance();
1422                } else {
1423                    grumble("No arguments are allowed in node()");
1424                    return null;
1425                }
1426            case Type.TEXT:
1427                if (empty) {
1428                    return NodeKindTest.TEXT;
1429                } else {
1430                    grumble("No arguments are allowed in text()");
1431                    return null;
1432                }
1433            case Type.COMMENT:
1434                if (empty) {
1435                    return NodeKindTest.COMMENT;
1436                } else {
1437                    grumble("No arguments are allowed in comment()");
1438                    return null;
1439                }
1440            case Type.NAMESPACE:
1441                grumble("No node test is defined for namespace nodes");
1442                return null;
1443            case Type.DOCUMENT:
1444                if (empty) {
1445                    return NodeKindTest.DOCUMENT;
1446                } else {
1447                    int innerType;
1448                    try {
1449                        innerType = getSystemType(t.currentTokenValue);
1450                    } catch (XPathException err) {
1451                        innerType = Type.EMPTY;
1452                    }
1453                    if (innerType != Type.ELEMENT) {
1454                        grumble("Argument to document-node() must be an element type descriptor");
1455                        return null;
1456                    }
1457                    NodeTest inner = parseKindTest();
1458                    expect(Token.RPAR);
1459                    nextToken();
1460                    return new DocumentNodeTest(inner);
1461                }
1462            case Type.PROCESSING_INSTRUCTION:
1463                if (empty) {
1464                    return NodeKindTest.PROCESSING_INSTRUCTION;
1465                } else if (t.currentToken == Token.STRING_LITERAL) {
1466                    try {
1467                        String JavaDoc[] parts = Name.getQNameParts(t.currentTokenValue);
1468                        if ("".equals(parts[0])) {
1469                            nameCode = makeNameCode(parts[1], false);
1470                        } else {
1471                            warning("No processing instruction name will ever contain a colon");
1472                            nameCode = env.getNamePool().allocate("prefix", "http://saxon.sf.net/ nonexistent namespace", "___invalid-name");
1473                        }
1474                    } catch (QNameException e) {
1475                        warning("No processing instruction will ever be named '" +
1476                                t.currentTokenValue + "'. " + e.getMessage());
1477                        nameCode = env.getNamePool().allocate("prefix", "http://saxon.sf.net/ nonexistent namespace", "___invalid-name");
1478                    }
1479                } else if (t.currentToken == Token.NAME) {
1480                    try {
1481                        String JavaDoc[] parts = Name.getQNameParts(t.currentTokenValue);
1482                        if ("".equals(parts[0])) {
1483                            nameCode = makeNameCode(parts[1], false);
1484                        } else {
1485                            grumble("Processing instruction name must not contain a colon");
1486                        }
1487                    } catch (QNameException e) {
1488                        grumble("Invalid processing instruction name. " + e.getMessage());
1489                    }
1490                } else {
1491                    grumble("Processing instruction name must be a QName or a string literal");
1492                }
1493                nextToken();
1494                expect(Token.RPAR);
1495                nextToken();
1496                return new NameTest(Type.PROCESSING_INSTRUCTION, nameCode, env.getNamePool());
1497
1498            case Type.ATTRIBUTE:
1499                // drop through
1500

1501            case Type.ELEMENT:
1502                String JavaDoc nodeName = "";
1503                if (empty) {
1504                    return NodeKindTest.makeNodeKindTest(primaryType);
1505                } else if (t.currentToken == Token.STAR || t.currentToken == Token.MULT) {
1506                    // allow for both representations of "*" to be safe
1507
if (schemaDeclaration) {
1508                        grumble("schema-element() and schema-attribute() must specify an actual name, not '*'");
1509                        return null;
1510                    }
1511                    nameCode = -1;
1512                } else if (t.currentToken == Token.NAME) {
1513                    nodeName = t.currentTokenValue;
1514                    nameCode = makeNameCode(t.currentTokenValue, true); // & 0xfffff;
1515
} else {
1516                    grumble("Unexpected " + Token.tokens[t.currentToken] + " after '(' in SequenceType");
1517                }
1518                String JavaDoc suri = null;
1519                if (nameCode != -1) {
1520                    suri = env.getNamePool().getURI(nameCode);
1521                }
1522                nextToken();
1523                if (t.currentToken == Token.SLASH) {
1524                    grumble("Schema context paths have been dropped from the language specification");
1525                } else if (t.currentToken == Token.RPAR) {
1526                    nextToken();
1527                    if (nameCode == -1) {
1528                        // element(*) or attribute(*)
1529
return NodeKindTest.makeNodeKindTest(primaryType);
1530                    } else {
1531                        NodeTest nameTest = null;
1532                        SchemaType schemaType = null;
1533                        if (primaryType == Type.ATTRIBUTE) {
1534                            // attribute(N) or schema-attribute(N)
1535
if (schemaDeclaration) {
1536                                SchemaDeclaration attributeDecl =
1537                                        env.getConfiguration().getAttributeDeclaration(nameCode & 0xfffff);
1538                                if (!env.isImportedSchema(suri)) {
1539                                    grumble("No schema has been imported for namespace '" + suri + '\'');
1540                                }
1541                                if (attributeDecl == null) {
1542                                    grumble("There is no declaration for attribute @" + nodeName + " in an imported schema");
1543                                } else {
1544                                    schemaType = attributeDecl.getType();
1545                                    nameTest = new NameTest(Type.ATTRIBUTE, nameCode, env.getNamePool());
1546                                }
1547                            } else {
1548                                nameTest = new NameTest(Type.ATTRIBUTE, nameCode, env.getNamePool());
1549                                return nameTest;
1550                            }
1551                        } else {
1552                            // element(N) or schema-element(N)
1553
if (schemaDeclaration) {
1554                                SchemaDeclaration elementDecl =
1555                                        env.getConfiguration().getElementDeclaration(nameCode & 0xfffff);
1556                                if (!env.isImportedSchema(suri)) {
1557                                    grumble("No schema has been imported for namespace '" + suri + '\'');
1558                                }
1559                                if (elementDecl == null) {
1560                                    grumble("There is no declaration for element <" + nodeName + "> in an imported schema");
1561                                } else {
1562                                    schemaType = elementDecl.getType();
1563                                    nameTest = env.getConfiguration().makeSubstitutionGroupTest(elementDecl);
1564
1565                                }
1566                            } else {
1567                                nameTest = new NameTest(Type.ELEMENT, nameCode, env.getNamePool());
1568                                return nameTest;
1569                            }
1570                        }
1571                        ContentTypeTest contentTest = null;
1572                        if (schemaType != null) {
1573                            contentTest = new ContentTypeTest(primaryType, schemaType, env.getConfiguration());
1574                        }
1575
1576                        if (contentTest == null) {
1577                            return nameTest;
1578                        } else {
1579                            return new CombinedNodeTest(nameTest, Token.INTERSECT, contentTest);
1580                        }
1581                    }
1582                } else if (t.currentToken == Token.COMMA) {
1583                    if (schemaDeclaration) {
1584                        grumble("schema-element() and schema-attribute() must have one argument only");
1585                        return null;
1586                    }
1587                    nextToken();
1588                    NodeTest result;
1589                    if (t.currentToken == Token.STAR) {
1590                        grumble("'*' is no longer permitted as the second argument of element() and attribute()");
1591                        return null;
1592                    } else if (t.currentToken == Token.NAME) {
1593                        SchemaType schemaType;
1594                        contentType = makeNameCode(t.currentTokenValue, true) & 0xfffff;
1595                        String JavaDoc uri = env.getNamePool().getURI(contentType);
1596                        String JavaDoc lname = env.getNamePool().getLocalName(contentType);
1597                        if (uri.equals(NamespaceConstant.SCHEMA) ||
1598                                NamespaceConstant.isXDTNamespace(uri)) {
1599                            schemaType = env.getConfiguration().getSchemaType(contentType);
1600                        } else {
1601                            if (!env.isImportedSchema(uri)) {
1602                                grumble("No schema has been imported for namespace '" + uri + '\'');
1603                            }
1604                            schemaType = env.getConfiguration().getSchemaType(contentType);
1605                        }
1606                        if (schemaType == null) {
1607                            grumble("Unknown type name " + lname);
1608                        }
1609                        if (primaryType == Type.ATTRIBUTE && schemaType.isComplexType()) {
1610                            grumble("An attribute cannot have a complex type");
1611                        }
1612                        ContentTypeTest typeTest = new ContentTypeTest(primaryType, schemaType, env.getConfiguration());
1613                        if (nameCode == -1) {
1614                            // this represents element(*,T) or attribute(*,T)
1615
result = typeTest;
1616                            if (primaryType == Type.ATTRIBUTE) {
1617                                nextToken();
1618                            } else {
1619                                // assert (primaryType == Type.ELEMENT);
1620
nextToken();
1621                                if (t.currentToken == Token.QMARK) {
1622                                     typeTest.setNillable(true);
1623                                     nextToken();
1624                                }
1625                            }
1626                        } else {
1627                            if (primaryType == Type.ATTRIBUTE) {
1628                                NodeTest nameTest = new NameTest(Type.ATTRIBUTE, nameCode, env.getNamePool());
1629                                result = new CombinedNodeTest(nameTest, Token.INTERSECT, typeTest);
1630                                nextToken();
1631                            } else {
1632                                // assert (primaryType == Type.ELEMENT);
1633
NodeTest nameTest = new NameTest(Type.ELEMENT, nameCode, env.getNamePool());
1634                                result = new CombinedNodeTest(nameTest, Token.INTERSECT, typeTest);
1635                                nextToken();
1636                                if (t.currentToken == Token.QMARK) {
1637                                     typeTest.setNillable(true);
1638                                     nextToken();
1639                                }
1640                            }
1641                        }
1642                    } else {
1643                        grumble("Unexpected " + Token.tokens[t.currentToken] + " after ',' in SequenceType");
1644                        return null;
1645                    }
1646
1647                    expect(Token.RPAR);
1648                    nextToken();
1649                    return result;
1650                } else {
1651                    grumble("Expected ')' or ',' in SequenceType");
1652                }
1653                return null;
1654            default:
1655                // can't happen!
1656
grumble("Unknown node kind");
1657                return null;
1658        }
1659    }
1660
1661    /**
1662     * Get a system type - that is, one whose name is a keyword rather than a QName. This includes the node
1663     * kinds such as element and attribute, the generic types node() and item(), and the pseudo-type empty()
1664     *
1665     * @param name
1666     * @exception net.sf.saxon.trans.StaticError
1667     */

1668    private static int getSystemType(String JavaDoc name) throws StaticError {
1669        if ("item".equals(name)) return Type.ITEM;
1670        else if ("document-node".equals(name)) return Type.DOCUMENT;
1671        else if ("element".equals(name)) return Type.ELEMENT;
1672        else if ("schema-element".equals(name)) return Type.ELEMENT;
1673        else if ("attribute".equals(name)) return Type.ATTRIBUTE;
1674        else if ("schema-attribute".equals(name)) return Type.ATTRIBUTE;
1675        else if ("text".equals(name)) return Type.TEXT;
1676        else if ("comment".equals(name)) return Type.COMMENT;
1677        else if ("processing-instruction".equals(name))
1678                                                   return Type.PROCESSING_INSTRUCTION;
1679        else if ("namespace".equals(name)) return Type.NAMESPACE;
1680        else if ("node".equals(name)) return Type.NODE;
1681        else if ("empty".equals(name)) return Type.EMPTY;
1682        else throw new StaticError("Unknown type " + name);
1683    }
1684
1685    /**
1686     * Parse a function call
1687     * function-name '(' ( Expression (',' Expression )* )? ')'
1688     *
1689     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1690     * @return the resulting subexpression
1691     */

1692
1693    private Expression parseFunctionCall() throws StaticError {
1694
1695        String JavaDoc fname = t.currentTokenValue;
1696        int offset = t.currentTokenStartOffset;
1697        ArrayList JavaDoc args = new ArrayList JavaDoc(10);
1698
1699        // the "(" has already been read by the Tokenizer: now parse the arguments
1700

1701        nextToken();
1702        if (t.currentToken!=Token.RPAR) {
1703            Expression arg = parseExprSingle();
1704            args.add(arg);
1705            while(t.currentToken==Token.COMMA) {
1706                nextToken();
1707                arg = parseExprSingle();
1708                args.add(arg);
1709            }
1710            expect(Token.RPAR);
1711        }
1712        nextToken();
1713
1714        if (scanOnly) {
1715            return StringValue.EMPTY_STRING;
1716        }
1717
1718        Expression[] arguments = new Expression[args.size()];
1719        args.toArray(arguments);
1720
1721        String JavaDoc[] parts;
1722        try {
1723            parts = Name.getQNameParts(fname);
1724        } catch (QNameException e) {
1725            grumble("Unknown prefix in function name " + fname + "()");
1726            return null;
1727        }
1728        String JavaDoc uri;
1729        if ("".equals(parts[0])) {
1730            uri = env.getDefaultFunctionNamespace();
1731        } else {
1732            try {
1733                uri = env.getURIForPrefix(parts[0]);
1734            } catch (XPathException err) {
1735                grumble(err.getMessage());
1736                return null;
1737            }
1738        }
1739        int nameCode = env.getNamePool().allocate(parts[0], uri, parts[1]);
1740        if (uri.equals(NamespaceConstant.SCHEMA)) {
1741            ItemType t = Type.getBuiltInItemType(uri, parts[1]);
1742            if (t instanceof BuiltInAtomicType && !env.isAllowedBuiltInType((BuiltInAtomicType)t)) {
1743                warning("The type " + fname + " is not recognized by a Basic XSLT Processor. " +
1744                        "Saxon permits it for the time being.");
1745            }
1746        }
1747        Expression fcall;
1748        try {
1749            fcall = env.getFunctionLibrary().bind(nameCode, uri, parts[1], arguments);
1750        } catch (XPathException err) {
1751            if (err.getErrorCodeLocalPart() == null) {
1752                err.setErrorCode("XPST0017");
1753            }
1754            grumble(err.getMessage(), err.getErrorCodeLocalPart());
1755            return null;
1756        }
1757        if (fcall == null) {
1758            String JavaDoc msg = "Cannot find a matching " + arguments.length +
1759                    "-argument function named " + env.getNamePool().getClarkName(nameCode) + "()";
1760            if (!env.getConfiguration().isAllowExternalFunctions()) {
1761                msg += ". Note: external function calls have been disabled";
1762            }
1763            if (env.isInBackwardsCompatibleMode()) {
1764                // treat this as a dynamic error to be reported only if the function call is executed
1765
DynamicError err = new DynamicError(msg);
1766                ErrorExpression exp = new ErrorExpression(err);
1767                setLocation(exp);
1768                return exp;
1769            }
1770            grumble(msg);
1771        }
1772        // A QName or NOTATION constructor function must be evaluated now, while we know the namespace context
1773
if (fcall instanceof CastExpression && ((AtomicType)fcall.getItemType()).isNamespaceSensitive()) {
1774            try {
1775                return ((CastExpression)fcall).doQNameCast(env);
1776            } catch (XPathException e) {
1777                grumble(e.getMessage());
1778            }
1779        }
1780        setLocation(fcall, offset);
1781        for (int a=0; a<arguments.length; a++) {
1782            ((ComputedExpression)fcall).adoptChildExpression(arguments[a]);
1783        }
1784        return makeTracer(offset, fcall, Location.FUNCTION_CALL, nameCode);
1785
1786    }
1787
1788
1789    //////////////////////////////////////////////////////////////////////////////////
1790
// Routines for handling range variables
1791
//////////////////////////////////////////////////////////////////////////////////
1792

1793    /**
1794     * Declare a range variable (record its existence within the parser).
1795     * A range variable is a variable declared within an expression, as distinct
1796     * from a variable declared in the context.
1797     *
1798     * @param declaration the VariableDeclaration to be added to the stack
1799     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1800     */

1801
1802    protected void declareRangeVariable(VariableDeclaration declaration) throws StaticError {
1803        if (rangeVariables == null) {
1804            rangeVariables = new Stack JavaDoc();
1805        }
1806        rangeVariables.push(declaration);
1807    }
1808
1809    /**
1810     * Note when the most recently declared range variable has gone out of scope
1811     */

1812
1813    protected void undeclareRangeVariable() {
1814        rangeVariables.pop();
1815    }
1816
1817    /**
1818     * Locate a range variable with a given name. (By "range variable", we mean a
1819     * variable declared within the expression where it is used.)
1820     *
1821     * @param fingerprint identifies the name of the range variable within the
1822     * name pool
1823     * @return null if not found (this means the variable is probably a
1824     * context variable); otherwise the relevant VariableDeclaration
1825     */

1826
1827    private VariableDeclaration findRangeVariable(int fingerprint) {
1828        if (rangeVariables==null) {
1829            return null;
1830        }
1831        for (int v=rangeVariables.size()-1; v>=0; v--) {
1832            VariableDeclaration b = (VariableDeclaration)rangeVariables.elementAt(v);
1833            if ((b.getNameCode() & 0xfffff) == fingerprint) {
1834                return b;
1835            }
1836        }
1837        return null; // not an in-scope range variable
1838
}
1839
1840    /**
1841     * Get the range variable stack. Used when parsing a nested subexpression
1842     * inside an attribute constructor
1843     */

1844
1845    public Stack JavaDoc getRangeVariableStack() {
1846        return rangeVariables;
1847    }
1848
1849    /**
1850     * Set the range variable stack. Used when parsing a nested subexpression
1851     * inside an attribute constructor.
1852     */

1853
1854    public void setRangeVariableStack(Stack JavaDoc stack) {
1855        rangeVariables = stack;
1856    }
1857
1858    //////////////////////////////////////////////////////////////////////////////////
1859
// PATTERNS //
1860
//////////////////////////////////////////////////////////////////////////////////
1861

1862
1863    /**
1864     * Parse a Union Pattern:<br>
1865     * pathPattern ( | pathPattern )*
1866     *
1867     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1868     * @return the pattern that results from parsing
1869     */

1870
1871    private Pattern parseUnionPattern() throws StaticError {
1872        Pattern exp1 = parsePathPattern();
1873
1874        while (t.currentToken == Token.UNION ) {
1875            if (t.currentTokenValue == "union") {
1876                grumble("Union operator in a pattern must be written as '|'");
1877            }
1878            nextToken();
1879            Pattern exp2 = parsePathPattern();
1880            exp1 = new UnionPattern(exp1, exp2);
1881        }
1882
1883        return exp1;
1884    }
1885
1886    /**
1887     * Parse a Location Path Pattern:
1888     *
1889     * @throws net.sf.saxon.trans.StaticError if any error is encountered
1890     * @return the pattern that results from parsing
1891     */

1892
1893    private Pattern parsePathPattern() throws StaticError {
1894
1895        Pattern prev = null;
1896        int connector = -1;
1897        boolean rootonly = false;
1898
1899        // special handling of stuff before the first component
1900

1901        switch(t.currentToken) {
1902            case Token.SLASH:
1903                connector = t.currentToken;
1904                nextToken();
1905                prev = new NodeTestPattern(NodeKindTest.makeNodeKindTest(Type.DOCUMENT));
1906                rootonly = true;
1907                break;
1908            case Token.SLSL: // leading double slash can't be ignored
1909
// because it changes the default priority
1910
connector = t.currentToken;
1911                nextToken();
1912                prev = new NodeTestPattern(NodeKindTest.makeNodeKindTest(Type.DOCUMENT));
1913                rootonly = false;
1914                break;
1915            default:
1916                break;
1917        }
1918
1919        while(true) {
1920            Pattern pat = null;
1921            switch(t.currentToken) {
1922                case Token.AXIS:
1923                    if ("child".equals(t.currentTokenValue)) {
1924                        nextToken();
1925                        pat = parsePatternStep(Type.ELEMENT);
1926                    } else if ("attribute".equals(t.currentTokenValue)) {
1927                        nextToken();
1928                        pat = parsePatternStep(Type.ATTRIBUTE);
1929                    } else {
1930                        grumble("Axis in pattern must be child or attribute");
1931                    }
1932                    break;
1933
1934                case Token.STAR:
1935                case Token.NAME:
1936                case Token.PREFIX:
1937                case Token.SUFFIX:
1938                    pat = parsePatternStep(Type.ELEMENT);
1939                    break;
1940
1941                case Token.NODEKIND:
1942                    pat = parsePatternStep(t.currentTokenValue=="attribute" ? Type.ATTRIBUTE : Type.ELEMENT);
1943                    break;
1944
1945                case Token.AT:
1946                    nextToken();
1947                    pat = parsePatternStep(Type.ATTRIBUTE);
1948                    break;
1949
1950                case Token.FUNCTION: // must be id(literal) or key(literal,literal)
1951
if (prev!=null) {
1952                        grumble("Function call may appear only at the start of a pattern");
1953                    }
1954                    if ("id".equals(t.currentTokenValue)) {
1955                        nextToken();
1956                        Expression idValue = null;
1957                        if (t.currentToken == Token.STRING_LITERAL) {
1958                            idValue = new StringValue(t.currentTokenValue);
1959                        } else if (t.currentToken == Token.DOLLAR) {
1960                            nextToken();
1961                            expect(Token.NAME);
1962                            int varNameCode = makeNameCode(t.currentTokenValue, false) & 0xfffff;
1963                            idValue = new VariableReference(env.bindVariable(varNameCode));
1964                        } else {
1965                            grumble("id value in pattern must be either a literal or a variable reference");
1966                        }
1967                        pat = new IDPattern(idValue);
1968                        nextToken();
1969                        expect(Token.RPAR);
1970                        nextToken();
1971                    } else if ("key".equals(t.currentTokenValue)) {
1972                        nextToken();
1973                        expect(Token.STRING_LITERAL);
1974                        String JavaDoc keyname = t.currentTokenValue;
1975                        nextToken();
1976                        expect(Token.COMMA);
1977                        nextToken();
1978                        Expression idValue = null;
1979                        if (t.currentToken == Token.STRING_LITERAL) {
1980                            idValue = new StringValue(t.currentTokenValue);
1981                        } else if (t.currentToken == Token.DOLLAR) {
1982                            nextToken();
1983                            expect(Token.NAME);
1984                            int varNameCode = makeNameCode(t.currentTokenValue, false) & 0xfffff;
1985                            idValue = new VariableReference(env.bindVariable(varNameCode));
1986                        } else {
1987                            grumble("key value must be either a literal or a variable reference");
1988                        }
1989                        pat = new KeyPattern(makeNameCode(keyname, false),
1990                                                idValue);
1991                        nextToken();
1992                        expect(Token.RPAR);
1993                        nextToken();
1994                    } else {
1995                        grumble("The only functions allowed in a pattern are id() and key()");
1996                    }
1997                    break;
1998
1999                default:
2000                    if (rootonly) { // the pattern was plain '/'
2001
return prev;
2002                    }
2003                    grumble("Unexpected token in pattern, found " + currentTokenDisplay());
2004            }
2005
2006            if (prev != null) {
2007                if (connector==Token.SLASH) {
2008                     ((LocationPathPattern)pat).parentPattern = prev;
2009                } else { // connector == SLSL
2010
((LocationPathPattern)pat).ancestorPattern = prev;
2011                }
2012            }
2013            connector = t.currentToken;
2014            rootonly = false;
2015            if (connector == Token.SLASH || connector == Token.SLSL) {
2016                prev = pat;
2017                nextToken();
2018            } else {
2019                return pat;
2020            }
2021        }
2022    }
2023
2024    /**
2025     * Parse a pattern step (after any axis name or @)
2026     *
2027     * @throws net.sf.saxon.trans.StaticError if any error is encountered
2028     * @param principalNodeType is ELEMENT if we're on the child axis, ATTRIBUTE for
2029     * the attribute axis
2030     * @return the pattern that results from parsing
2031     */

2032
2033    private Pattern parsePatternStep(short principalNodeType) throws StaticError {
2034        LocationPathPattern step = new LocationPathPattern();
2035        NodeTest test = parseNodeTest(principalNodeType);
2036        if (test instanceof AnyNodeTest) {
2037            // handle node() and @node() specially
2038
if (principalNodeType == Type.ELEMENT) {
2039                // this means we're on the CHILD axis
2040
test = new AnyChildNodePattern();
2041            } else {
2042                // we're on the attribute axis
2043
test = NodeKindTest.makeNodeKindTest(principalNodeType);
2044            }
2045        }
2046
2047        // Deal with nonsense patterns such as @comment() or child::attribute(). These
2048
// are legal, but will never match anything.
2049

2050        int kind = test.getPrimitiveType();
2051        if (principalNodeType == Type.ELEMENT &&
2052                (kind == Type.ATTRIBUTE || kind == Type.NAMESPACE)) {
2053            test = new NoNodeTest();
2054        } else if (principalNodeType == Type.ATTRIBUTE &&
2055                (kind == Type.COMMENT || kind == Type.TEXT ||
2056                kind == Type.PROCESSING_INSTRUCTION || kind == Type.ELEMENT ||
2057                kind == Type.DOCUMENT)) {
2058            test = new NoNodeTest();
2059        }
2060
2061        step.nodeTest = test;
2062        parseFilters(step);
2063        return step;
2064    }
2065
2066    /**
2067     * Test to see if there are filters for a Pattern, if so, parse them
2068     *
2069     * @param path the LocationPathPattern to which the filters are to be
2070     * added
2071     * @throws net.sf.saxon.trans.StaticError if any error is encountered
2072     */

2073
2074    private void parseFilters(LocationPathPattern path) throws StaticError {
2075        while (t.currentToken == Token.LSQB) {
2076            nextToken();
2077            Expression qual = parseExpression();
2078            expect(Token.RSQB);
2079            nextToken();
2080            path.addFilter(qual);
2081        }
2082    }
2083
2084    // Helper methods to access the static context
2085

2086    /**
2087     * Make a NameCode, using this Element as the context for namespace resolution
2088     *
2089     * @throws net.sf.saxon.trans.StaticError if the name is invalid, or the prefix
2090     * undeclared
2091     * @param qname The name as written, in the form "[prefix:]localname"
2092     * @param useDefault Defines the action when there is no prefix. If
2093     * true, use the default namespace URI for element names. If false,
2094     * use no namespace URI (as for attribute names).
2095     * @return the namecode, which can be used to identify this name in the
2096     * name pool
2097     */

2098
2099    public final int makeNameCode(String JavaDoc qname, boolean useDefault) throws StaticError {
2100        if (scanOnly) {
2101            return -1;
2102        }
2103        try {
2104            String JavaDoc[] parts = Name.getQNameParts(qname);
2105            String JavaDoc prefix = parts[0];
2106            if ("".equals(prefix)) {
2107                short uricode = 0;
2108                if (useDefault) {
2109                    uricode = env.getDefaultElementNamespace();
2110                }
2111                return env.getNamePool().allocate(prefix, uricode, qname);
2112            } else {
2113                try {
2114                    String JavaDoc uri = env.getURIForPrefix(prefix);
2115                    if (uri == null) {
2116                        grumble("Undeclared namespace prefix " + Err.wrap(prefix));
2117                        return -1;
2118                    }
2119                    return env.getNamePool().allocate(prefix, uri, parts[1]);
2120                } catch (XPathException err) {
2121                    grumble(err.getMessage());
2122                    return -1;
2123                }
2124            }
2125        } catch (QNameException e) {
2126            //throw new XPathException.Static(e.getMessage());
2127
grumble(e.getMessage());
2128            return -1;
2129        }
2130    }
2131
2132    /**
2133     * Make a NameTest, using the static context for namespace resolution
2134     *
2135     * @param nodeType the type of node required (identified by a constant in
2136     * class Type)
2137     * @param qname the lexical QName of the required node
2138     * @param useDefault true if the default namespace should be used when
2139     * the QName is unprefixed
2140     * @throws net.sf.saxon.trans.StaticError if the QName is invalid
2141     * @return a NameTest, representing a pattern that tests for a node of a
2142     * given node kind and a given name
2143     */

2144
2145    public NameTest makeNameTest(short nodeType, String JavaDoc qname, boolean useDefault)
2146            throws StaticError {
2147        int nameCode = makeNameCode(qname, useDefault);
2148        NameTest nt = new NameTest(nodeType, nameCode, env.getNamePool());
2149        //nt.setOriginalText(qname);
2150
return nt;
2151    }
2152
2153    /**
2154     * Make a NamespaceTest (name:*)
2155     *
2156     * @param nodeType integer code identifying the type of node required
2157     * @param prefix the namespace prefix
2158     * @throws net.sf.saxon.trans.StaticError if the namespace prefix is not declared
2159     * @return the NamespaceTest, a pattern that matches all nodes in this
2160     * namespace
2161     */

2162
2163    public NamespaceTest makeNamespaceTest(short nodeType, String JavaDoc prefix)
2164            throws StaticError {
2165        if (scanOnly) {
2166            // return an arbitrary namespace if we're only doing a syntax check
2167
return new NamespaceTest(env.getNamePool(), nodeType, NamespaceConstant.SAXON);
2168        }
2169
2170        try {
2171            NamespaceTest nt = new NamespaceTest(env.getNamePool(), nodeType, env.getURIForPrefix(prefix));
2172            //nt.setOriginalText(prefix + ":*");
2173
return nt;
2174        } catch (XPathException e) {
2175            // env.getURIForPrefix can return a dynamic error
2176
grumble(e.getMessage());
2177            return null;
2178        }
2179    }
2180
2181    /**
2182     * Make a LocalNameTest (*:name)
2183     *
2184     * @param nodeType the kind of node to be matched
2185     * @param localName the requred local name
2186     * @throws net.sf.saxon.trans.StaticError if the local name is invalid
2187     * @return a LocalNameTest, a pattern which matches all nodes of a given
2188     * local name, regardless of namespace
2189     */

2190
2191    public LocalNameTest makeLocalNameTest(short nodeType, String JavaDoc localName)
2192            throws StaticError {
2193        if (!XMLChar.isValidNCName(localName)) {
2194            grumble("Local name [" + localName + "] contains invalid characters");
2195        }
2196        return new LocalNameTest(env.getNamePool(), nodeType, localName);
2197    }
2198
2199    /**
2200     * Set location information on an expression. At present this consists of a simple
2201     * line number. Needed mainly for XQuery.
2202     */

2203
2204    protected void setLocation(Expression exp) {
2205        if (exp instanceof ComputedExpression) {
2206            setLocation(exp, t.currentTokenStartOffset);
2207        }
2208    }
2209
2210    /**
2211     * Set location information on an expression. At present only the line number
2212     * is retained. Needed mainly for XQuery. This version of the method supplies an
2213     * explicit offset (character position within the expression or query), which the tokenizer
2214     * can convert to a line number and column number.
2215     */

2216
2217    protected void setLocation(Expression exp, int offset) {
2218        // TODO: we are losing the column position and retaining only the line number
2219
int line = t.getLineNumber(offset);
2220        if (exp instanceof ComputedExpression && ((ComputedExpression)exp).getLocationId()==-1) {
2221            int loc = env.getLocationMap().allocateLocationId(env.getSystemId(), line);
2222            ComputedExpression cexp = (ComputedExpression)exp;
2223            cexp.setLocationId(loc);
2224            // add a temporary container to provide location information
2225
if (cexp.getParentExpression() == null) {
2226                TemporaryContainer container = new TemporaryContainer(env.getLocationMap(), loc);
2227                cexp.setParentExpression(container);
2228            }
2229        }
2230    }
2231
2232    /**
2233     * If tracing, wrap an instruction in a trace instruction
2234     */

2235
2236    protected Expression makeTracer(int startOffset, Expression exp, int construct, int objectNameCode) {
2237        if (env.getConfiguration().getTraceListener() != null) {
2238            TraceExpression trace = new TraceExpression(exp);
2239            long lc = t.getLineAndColumn(startOffset);
2240            trace.setLineNumber((int)(lc>>32));
2241            trace.setColumnNumber((int)(lc&0x7fffffff));
2242            trace.setSystemId(env.getSystemId());
2243            trace.setNamespaceResolver(env.getNamespaceResolver());
2244            trace.setConstructType(construct);
2245            trace.setObjectNameCode(objectNameCode);
2246            return trace;
2247        } else {
2248            return exp;
2249        }
2250    }
2251
2252    /**
2253     * Test whether the current token is a given keyword.
2254     * @param s The string to be compared with the current token
2255     * @return true if they are the same
2256     */

2257
2258    protected boolean isKeyword(String JavaDoc s) {
2259        return (t.currentToken == Token.NAME && t.currentTokenValue.equals(s));
2260    }
2261
2262    public void setScanOnly(boolean scanOnly) {
2263        this.scanOnly = scanOnly;
2264    }
2265
2266    public static class ForClause {
2267
2268        public RangeVariableDeclaration rangeVariable;
2269        public RangeVariableDeclaration positionVariable;
2270        public Expression sequence;
2271        public SequenceType requiredType;
2272        public int offset;
2273    }
2274
2275    protected static class TemporaryContainer implements Container, LocationProvider {
2276        private LocationMap map;
2277        private int locationId;
2278
2279        public TemporaryContainer(LocationMap map, int locationId) {
2280            this.map = map;
2281            this.locationId = locationId;
2282        }
2283
2284        public Executable getExecutable() {
2285            return null;
2286        }
2287
2288        public LocationProvider getLocationProvider() {
2289            return map;
2290        }
2291
2292        public String JavaDoc getPublicId() {
2293            return null;
2294        }
2295
2296        public String JavaDoc getSystemId() {
2297            return map.getSystemId(locationId);
2298        }
2299
2300        public int getLineNumber() {
2301            return map.getLineNumber(locationId);
2302        }
2303
2304        public int getColumnNumber() {
2305            return -1;
2306        }
2307
2308        public String JavaDoc getSystemId(int locationId) {
2309            return getSystemId();
2310        }
2311
2312        public int getLineNumber(int locationId) {
2313            return getLineNumber();
2314        }
2315    }
2316
2317}
2318
2319//
2320
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
2321
// you may not use this file except in compliance with the License. You may obtain a copy of the
2322
// License at http://www.mozilla.org/MPL/
2323
//
2324
// Software distributed under the License is distributed on an "AS IS" basis,
2325
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
2326
// See the License for the specific language governing rights and limitations under the License.
2327
//
2328
// The Original Code is: all this file.
2329
//
2330
// The Initial Developer of the Original Code is Michael H. Kay.
2331
//
2332
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
2333
//
2334
// Contributor(s): none.
2335
//
2336
Popular Tags