KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > query > QueryParser


1 package net.sf.saxon.query;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.Err;
5 import net.sf.saxon.StandardURIResolver;
6 import net.sf.saxon.event.PipelineConfiguration;
7 import net.sf.saxon.expr.*;
8 import net.sf.saxon.functions.*;
9 import net.sf.saxon.instruct.*;
10 import net.sf.saxon.om.*;
11 import net.sf.saxon.pattern.NodeTest;
12 import net.sf.saxon.sort.FixedSortKeyDefinition;
13 import net.sf.saxon.sort.TupleExpression;
14 import net.sf.saxon.sort.TupleSorter;
15 import net.sf.saxon.style.AttributeValueTemplate;
16 import net.sf.saxon.style.StandardNames;
17 import net.sf.saxon.trace.Location;
18 import net.sf.saxon.trans.IndependentContext;
19 import net.sf.saxon.trans.StaticError;
20 import net.sf.saxon.trans.XPathException;
21 import net.sf.saxon.trans.DynamicError;
22 import net.sf.saxon.type.ItemType;
23 import net.sf.saxon.type.Type;
24 import net.sf.saxon.value.*;
25
26 import javax.xml.transform.OutputKeys JavaDoc;
27 import javax.xml.transform.TransformerConfigurationException JavaDoc;
28 import javax.xml.transform.TransformerException JavaDoc;
29 import javax.xml.transform.stream.StreamSource JavaDoc;
30 import java.util.*;
31 import java.net.URISyntaxException JavaDoc;
32
33 /**
34  * This class defines extensions to the XPath parser to handle the additional
35  * syntax supported in XQuery
36  */

37 class QueryParser extends ExpressionParser {
38
39     private boolean preserveSpace = false;
40     private boolean defaultEmptyLeast = true;
41
42     private int errorCount = 0;
43
44     protected Executable executable;
45
46     private boolean foundCopyNamespaces = false;
47     private boolean foundBoundarySpaceDeclaration = false;
48     private boolean foundOrderingDeclaration = false;
49     private boolean foundEmptyOrderingDeclaration = false;
50     private boolean foundDefaultCollation = false;
51     private boolean foundConstructionDeclaration = false;
52     private boolean foundDefaultFunctionNamespace = false;
53     private boolean foundDefaultElementNamespace = false;
54     private boolean foundBaseURIDeclaration = false;
55
56     public Set importedModules = new HashSet(5);
57
58     private Expression defaultValue = null;
59
60     /**
61      * Protected Constructor: this class should be instantiated via the StaticQueryContext
62      */

63
64     protected QueryParser() {};
65
66     /**
67      * Create an XQueryExpression
68      */

69
70     public XQueryExpression makeXQueryExpression(String JavaDoc query,
71                                                  StaticQueryContext staticContext,
72                                                  Configuration config) throws XPathException {
73         try {
74             query = normalizeLineEndings(query);
75             Executable exec = new Executable();
76
77             Properties outputProps = new Properties();
78             outputProps.setProperty(OutputKeys.METHOD, "xml");
79             outputProps.setProperty(OutputKeys.INDENT, "yes");
80             exec.setDefaultOutputProperties(outputProps);
81
82             exec.setLocationMap(new LocationMap());
83             exec.setConfiguration(config);
84             exec.setFunctionLibrary(new ExecutableFunctionLibrary(config));
85             // this will be changed later
86
exec.setHostLanguage(Configuration.XQUERY);
87             setExecutable(exec);
88             staticContext.setExecutable(exec);
89             Expression exp = parseQuery(query, 0, Token.EOF, staticContext);
90             if (exp instanceof ComputedExpression) {
91                 int loc = env.getLocationMap().allocateLocationId(env.getSystemId(), 1);
92                 ((ComputedExpression)exp).setParentExpression(new TemporaryContainer(staticContext.getLocationMap(), loc));
93             }
94             exec.fixupQueryModules(staticContext);
95             XQueryExpression queryExp = new XQueryExpression(exp, exec, staticContext, config);
96             exp = queryExp.getExpression();
97             DocumentInstr docInstruction;
98             if (exp instanceof DocumentInstr) {
99                 docInstruction = (DocumentInstr) exp;
100             } else {
101                 docInstruction = new DocumentInstr(false, null, staticContext.getSystemId());
102                 docInstruction.setContentExpression(exp);
103                 setLocation(docInstruction, 1);
104                 //makeContentConstructor(exp, docInstruction, 1);
105
}
106             queryExp.setDocumentInstruction(docInstruction);
107
108             // Make the function library that's available at run-time (e.g. for saxon:evaluate()). This includes
109
// all user-defined functions regardless of which module they are in
110

111             FunctionLibrary userlib = exec.getFunctionLibrary();
112             FunctionLibraryList lib = new FunctionLibraryList();
113             lib.addFunctionLibrary(new SystemFunctionLibrary(SystemFunctionLibrary.XPATH_ONLY));
114             lib.addFunctionLibrary(config.getVendorFunctionLibrary());
115             lib.addFunctionLibrary(new ConstructorFunctionLibrary(config));
116             if (config.isAllowExternalFunctions()) {
117                 lib.addFunctionLibrary(config.getExtensionBinder());
118             }
119             lib.addFunctionLibrary(userlib);
120             exec.setFunctionLibrary(lib);
121
122             return queryExp;
123         } catch (XPathException e) {
124             if (!e.hasBeenReported()) {
125                 e = StaticError.makeStaticError(e);
126                 reportError((StaticError)e);
127             }
128             throw e;
129         }
130     }
131
132     /**
133      * Normalize line endings in the source query. We follow the XML 1.1 rules.
134      */

135
136     private static String JavaDoc normalizeLineEndings(String JavaDoc in) {
137         if (in.indexOf((char)0xa) < 0 && in.indexOf((char)0x85) < 0 && in.indexOf((char)0x2028) < 0) {
138             return in;
139         }
140         FastStringBuffer sb = new FastStringBuffer(in.length());
141         for (int i=0; i<in.length(); i++) {
142             char ch = in.charAt(i);
143             switch (ch) {
144                 case 0x85:
145                 case 0x2028:
146                     sb.append((char)0xa);
147                     break;
148                 case 0xd:
149                     if (i<in.length()-1 && (in.charAt(i+1)==(char)0xa || in.charAt(i+1)==(char)0x85)) {
150                         sb.append((char)0xa);
151                         i++;
152                     } else {
153                         sb.append((char)0xa);
154                     }
155                     break;
156                 default:
157                     sb.append(ch);
158             }
159         }
160         return sb.toString();
161     }
162
163     /**
164      * Get the executable containing this expression.
165      */

166
167     public Executable getExecutable() {
168         return executable;
169     }
170
171     /**
172      * Set the executable used for this query expression
173      */

174
175     public void setExecutable(Executable exec) {
176         executable = exec;
177     }
178
179
180     /**
181      * Parse a top-level Query.
182      * Prolog? Expression
183      * @param queryString The text of the query
184      * @param start Offset of the start of the query
185      * @param terminator Token expected to follow the query (usually Token.EOF)
186      * @param env The static context
187      * @exception net.sf.saxon.trans.XPathException if the expression contains a syntax error
188      * @return the Expression object that results from parsing
189      */

190
191     private Expression parseQuery(String JavaDoc queryString,
192                                        int start,
193                                        int terminator,
194                                        StaticQueryContext env) throws XPathException {
195         this.env = env;
196         this.language = XQUERY;
197         t = new Tokenizer();
198         try {
199             t.tokenize(queryString, start, -1, 1);
200         } catch (StaticError err) {
201             grumble(err.getMessage());
202         }
203         parseVersionDeclaration();
204         parseProlog();
205         Expression exp = parseExpression();
206         if (t.currentToken != terminator) {
207             grumble("Unexpected token " + currentTokenDisplay() + " beyond end of query");
208         }
209         setLocation(exp);
210         if (errorCount == 0) {
211 // try {
212
// env.bindUnboundFunctionCalls();
213
// env.fixupGlobalVariables(env.getGlobalStackFrameMap());
214
// env.fixupGlobalFunctions();
215
// } catch (XPathException err) {
216
// try {
217
// errorCount++;
218
// env.getConfiguration().getErrorListener().fatalError(err);
219
// } catch (TransformerException err2) {
220
// if (err2 instanceof XPathException) {
221
// throw (XPathException) err2;
222
// } else {
223
// throw new StaticError(err2);
224
// }
225
// }
226
// }
227
}
228         if (errorCount == 0) {
229             return exp;
230         } else {
231             StaticError err = new StaticError("One or more static errors were reported during query analysis");
232             err.setHasBeenReported();
233             throw err;
234         }
235     }
236
237     /**
238      * Parse a library module.
239      * Prolog? Expression
240      * @param queryString The text of the library module.
241      * @param env The static context. The result of parsing
242      * a library module is that the static context is populated with a set of function
243      * declarations and variable declarations. Each library module must have its own
244      * static context objext.
245      * @throws net.sf.saxon.trans.StaticError if the expression contains a syntax error
246      */

247
248     public final void parseLibraryModule(String JavaDoc queryString, StaticQueryContext env)
249             throws StaticError {
250         this.env = env;
251         this.executable = env.getExecutable();
252         t = new Tokenizer();
253         try {
254             t.tokenize(queryString, 0, -1, 1);
255         } catch (StaticError err) {
256             grumble(err.getMessage());
257         }
258         parseVersionDeclaration();
259         parseModuleDeclaration();
260         parseProlog();
261         if (t.currentToken != Token.EOF) {
262             grumble("Unrecognized content found after the variable and function declarations in a library module");
263         }
264 // if (errorCount == 0) {
265
// env.bindUnboundFunctionCalls();
266
// env.fixupGlobalVariables(env.getExecutable().getGlobalVariableMap());
267
// try {
268
// env.fixupGlobalFunctions();
269
// } catch (XPathException err) {
270
// throw err.makeStatic();
271
// }
272
// }
273
if (errorCount != 0) {
274             throw new StaticError("Static errors were reported in the imported library module");
275         }
276     }
277
278     /**
279      * Report a static error
280      *
281      * @param message the error message
282      * @exception net.sf.saxon.trans.StaticError always thrown: an exception containing the
283      * supplied message
284      */

285
286     protected void grumble(String JavaDoc message, String JavaDoc errorCode) throws StaticError {
287         String JavaDoc s = t.recentText();
288         ExpressionLocation loc = makeLocator();
289         String JavaDoc prefix = getLanguage() +
290                 ("XPST0003".equals(errorCode) ? " syntax error " : " static error ") +
291                 (message.startsWith("...") ? "near" : "in") +
292                 " #" + s + "#:\n ";
293         StaticError exception = new StaticError(prefix + message);
294         exception.setErrorCode(errorCode);
295         exception.setLocator(loc);
296         reportError(exception);
297     }
298
299     private void reportError(StaticError exception) throws StaticError {
300         errorCount++;
301         try {
302             env.getConfiguration().getErrorListener().fatalError(exception);
303         } catch (TransformerException JavaDoc err) {
304             if (err instanceof StaticError) {
305                 throw (StaticError) err;
306             } else {
307                 throw new StaticError(err);
308             }
309         }
310         throw exception;
311     }
312
313     /**
314      * Make a Locator object representing the current parsing location
315      * @return a Locator
316      */

317     private ExpressionLocation makeLocator() {
318         int line = t.getLineNumber();
319         int column = t.getColumnNumber();
320
321         ExpressionLocation loc = new ExpressionLocation();
322         loc.setSystemId(env.getSystemId());
323         loc.setLineNumber(line);
324         loc.setColumnNumber(column);
325         return loc;
326     }
327
328     /**
329      * Parse the version declaration if present.
330      * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
331      */

332     private void parseVersionDeclaration() throws StaticError {
333         if (t.currentToken == Token.XQUERY_VERSION) {
334             nextToken();
335             expect(Token.STRING_LITERAL);
336             if (!("1.0".equals(t.currentTokenValue))) {
337                 grumble("XQuery version must be 1.0", "XQST0031");
338             }
339             nextToken();
340             if ("encoding".equals(t.currentTokenValue)) {
341                 nextToken();
342                 expect(Token.STRING_LITERAL);
343                 // we ignore the encoding now: it was handled earlier, while decoding the byte stream
344
nextToken();
345             }
346             expect(Token.SEMICOLON);
347             nextToken();
348         }
349     }
350
351     /**
352      * In a library module, parse the module declaration
353      * Syntax: <"module" "namespace"> prefix "=" uri ";"
354      * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
355      */

356
357     private void parseModuleDeclaration() throws StaticError {
358         expect(Token.MODULE_NAMESPACE);
359         nextToken();
360         expect(Token.NAME);
361         String JavaDoc prefix = t.currentTokenValue;
362         checkProhibitedPrefixes(prefix);
363         nextToken();
364         expect(Token.EQUALS);
365         nextToken();
366         expect(Token.STRING_LITERAL);
367         String JavaDoc uri = t.currentTokenValue;
368         nextToken();
369         expect(Token.SEMICOLON);
370         nextToken();
371         try {
372             ((StaticQueryContext) env).declarePassiveNamespace(prefix, uri, true);
373         } catch (StaticError err) {
374             err.setLocator(makeLocator());
375             reportError(err);
376         }
377         ((StaticQueryContext) env).setModuleNamespace(uri);
378     }
379
380     /**
381      * Parse the query prolog. This method, and its subordinate methods which handle
382      * individual declarations in the prolog, cause the static context to be updated
383      * with relevant context information. On exit, t.currentToken is the first token
384      * that is not recognized as being part of the prolog.
385      * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
386      */

387
388     private void parseProlog() throws StaticError {
389         boolean allowSetters = true;
390         boolean allowModuleDecl = true;
391         boolean allowDeclarations = true;
392         while (true) {
393             try {
394                 if (t.currentToken == Token.MODULE_NAMESPACE) {
395                     String JavaDoc uri = ((StaticQueryContext) env).getModuleNamespace();
396                     if (uri == null) {
397                         grumble("Module declaration must not be used in a main module");
398                     } else {
399                         grumble("Module declaration appears more than once");
400                     }
401                     if (!allowModuleDecl) {
402                         grumble("Module declaration must precede other declarations in the query prolog");
403                     }
404                 }
405                 allowModuleDecl = false;
406                 if (t.currentToken == Token.DECLARE_NAMESPACE) {
407                     if (!allowDeclarations) {
408                         grumble("Namespace declarations cannot follow variables, functions, or options");
409                     }
410                     allowSetters = false;
411                     parseNamespaceDeclaration();
412                 } else if (t.currentToken == Token.DECLARE_DEFAULT) {
413                     nextToken();
414                     expect(Token.NAME);
415                     if (t.currentTokenValue == "element") {
416                         if (!allowDeclarations) {
417                             grumble("Namespace declarations cannot follow variables, functions, or options");
418                         }
419                         allowSetters = false;
420                         parseDefaultElementNamespace();
421                     } else if (t.currentTokenValue == "function") {
422                         if (!allowDeclarations) {
423                             grumble("Namespace declarations cannot follow variables, functions, or options");
424                         }
425                         allowSetters = false;
426                         parseDefaultFunctionNamespace();
427                     } else if (t.currentTokenValue == "collation") {
428                         if (!allowDeclarations) {
429                             grumble("Collation declarations must appear earlier in the prolog");
430                         }
431                         parseDefaultCollation();
432                     } else if (t.currentTokenValue == "order") {
433                         if (!allowDeclarations) {
434                             grumble("Order declarations must appear earlier in the prolog");
435                         }
436                         parseDefaultOrder();
437                     } else {
438                         grumble("After 'declare default', expected 'element', 'function', or 'collation'");
439                     }
440                 } else if (t.currentToken == Token.DECLARE_BOUNDARY_SPACE) {
441                     if (!allowSetters) {
442                         grumble("'declare boundary-space' must appear earlier in the query prolog");
443                     }
444                     parseBoundarySpaceDeclaration();
445                 } else if (t.currentToken == Token.DECLARE_ORDERING) {
446                     if (!allowSetters) {
447                         grumble("'declare ordering' must appear earlier in the query prolog");
448                     }
449                     parseOrderingDeclaration();
450                 } else if (t.currentToken == Token.DECLARE_COPY_NAMESPACES) {
451                     if (!allowSetters) {
452                         grumble("'declare copy-namespaces' must appear earlier in the query prolog");
453                     }
454                     parseCopyNamespacesDeclaration();
455                 } else if (t.currentToken == Token.DECLARE_BASEURI) {
456                     if (!allowSetters) {
457                         grumble("'declare base-uri' must appear earlier in the query prolog");
458                     }
459                     parseBaseURIDeclaration();
460                 } else if (t.currentToken == Token.IMPORT_SCHEMA) {
461                     allowSetters = false;
462                     if (!allowDeclarations) {
463                         grumble("Import schema must appear earlier in the prolog");
464                     }
465                     parseSchemaImport();
466                 } else if (t.currentToken == Token.IMPORT_MODULE) {
467                     allowSetters = false;
468                     if (!allowDeclarations) {
469                         grumble("Import module must appear earlier in the prolog");
470                     }
471                     parseModuleImport();
472                 } else if (t.currentToken == Token.DECLARE_VARIABLE) {
473                     allowSetters = false;
474                     allowDeclarations = false;
475                     parseVariableDeclaration();
476                 } else if (t.currentToken == Token.DECLARE_FUNCTION) {
477                     allowSetters = false;
478                     allowDeclarations = false;
479                     parseFunctionDeclaration();
480                 } else if (t.currentToken == Token.DECLARE_OPTION) {
481                     allowSetters = false;
482                     allowDeclarations = false;
483                     parseOptionDeclaration();
484                 } else if (t.currentToken == Token.DECLARE_CONSTRUCTION) {
485                     if (!allowSetters) {
486                         grumble("'declare construction' must appear earlier in the query prolog");
487                     }
488                     parseConstructionDeclaration();
489                 } else {
490                     break;
491                 }
492                 expect(Token.SEMICOLON);
493                 nextToken();
494             } catch (StaticError err) {
495                 if (err.getLocator() == null) {
496                     err.setLocator(makeLocator());
497                 }
498                 if (!err.hasBeenReported()) {
499                     errorCount++;
500                     try {
501                         env.getConfiguration().getErrorListener().fatalError(err);
502                     } catch (TransformerException JavaDoc err2) {
503                         if (err2 instanceof StaticError) {
504                             throw (StaticError) err2;
505                         } else {
506                             throw new StaticError(err);
507                         }
508                     }
509                 }
510                 // we've reported an error, attempt to recover by skipping to the
511
// next semicolon
512
while (t.currentToken != Token.SEMICOLON) {
513                     nextToken();
514                     if (t.currentToken == Token.EOF) {
515                         return;
516                     } else if (t.currentToken == Token.RCURLY) {
517                         t.lookAhead();
518                     } else if (t.currentToken == Token.TAG) {
519                         parsePseudoXML(true);
520                     }
521                 }
522                 nextToken();
523             }
524         }
525     }
526
527     private void parseDefaultCollation() throws StaticError {
528         // <"default" "collation"> StringLiteral
529
if (foundDefaultCollation) {
530             grumble("default collation appears more than once", "XQST0038");
531         }
532         foundDefaultCollation = true;
533         nextToken();
534         expect(Token.STRING_LITERAL);
535         String JavaDoc uri = t.currentTokenValue;
536         try {
537             ((StaticQueryContext) env).declareDefaultCollation(uri);
538         } catch (XPathException err) {
539             grumble(err.getMessage());
540         }
541         nextToken();
542     }
543
544     /**
545      * parse "declare default order empty (least|greatest)"
546      */

547     private void parseDefaultOrder() throws StaticError {
548         if (foundEmptyOrderingDeclaration) {
549             grumble("empty ordering declaration appears more than once", "XQST0069");
550         }
551         foundEmptyOrderingDeclaration = true;
552         nextToken();
553         if (!isKeyword("empty")) {
554             grumble("After 'declare default order', expected keyword 'empty'");
555         }
556         nextToken();
557         if (isKeyword("least")) {
558             defaultEmptyLeast = true;
559         } else if (isKeyword("greatest")) {
560             defaultEmptyLeast = false;
561         } else {
562             grumble("After 'declare default order empty', expected keyword 'least' or 'greatest'");
563         }
564         nextToken();
565     }
566
567     /**
568      * Parse the "declare xmlspace" declaration.
569      * Syntax: <"declare" "boundary-space"> ("preserve" | "strip")
570      * @throws net.sf.saxon.trans.StaticError
571      */

572
573     private void parseBoundarySpaceDeclaration() throws StaticError {
574         if (foundBoundarySpaceDeclaration) {
575             grumble("'declare boundary-space' appears more than once", "XQST0068");
576         }
577         foundBoundarySpaceDeclaration = true;
578         nextToken();
579         expect(Token.NAME);
580         if ("preserve".equals(t.currentTokenValue)) {
581             preserveSpace = true;
582         } else if ("strip".equals(t.currentTokenValue)) {
583             preserveSpace = false;
584         } else {
585             grumble("boundary-space must be 'preserve' or 'strip'");
586         }
587         nextToken();
588     }
589
590     /**
591      * Parse the "declare ordering" declaration.
592      * Syntax: <"declare" "ordering"> ("ordered" | "unordered")
593      * @throws net.sf.saxon.trans.StaticError
594      */

595
596     private void parseOrderingDeclaration() throws StaticError {
597         if (foundOrderingDeclaration) {
598             grumble("ordering mode declaration appears more than once", "XQST0065");
599         }
600         foundOrderingDeclaration = true;
601         nextToken();
602         expect(Token.NAME);
603         if ("ordered".equals(t.currentTokenValue)) {
604             // no action
605
} else if ("unordered".equals(t.currentTokenValue)) {
606             // no action
607
} else {
608             grumble("ordering mode must be 'ordered' or 'unordered'");
609         }
610         nextToken();
611     }
612
613     /**
614      * Parse the "declare copy-namespaces" declaration.
615      * Syntax: <"declare" "copy-namespaces"> ("preserve" | "no-preserve") "," ("inherit" | "no-inherit")
616      * @throws net.sf.saxon.trans.StaticError
617      */

618
619     private void parseCopyNamespacesDeclaration() throws StaticError {
620         if (foundCopyNamespaces) {
621             grumble("declare inherit-namespaces appears more than once", "XQST0055");
622         }
623         foundCopyNamespaces = true;
624         nextToken();
625         expect(Token.NAME);
626         if ("preserve".equals(t.currentTokenValue)) {
627             ((StaticQueryContext)env).setPreserveNamespaces(true);
628         } else if ("no-preserve".equals(t.currentTokenValue)) {
629             ((StaticQueryContext)env).setPreserveNamespaces(false);
630         } else {
631             grumble("copy-namespaces must be followed by 'preserve' or 'no-preserve'");
632         }
633         nextToken();
634         expect(Token.COMMA);
635         nextToken();
636         expect(Token.NAME);
637         if ("inherit".equals(t.currentTokenValue)) {
638             ((StaticQueryContext)env).setInheritNamespaces(true);
639         } else if ("no-inherit".equals(t.currentTokenValue)) {
640             ((StaticQueryContext)env).setInheritNamespaces(false);
641         } else {
642             grumble("After the comma in the copy-namespaces declaration, expected 'inherit' or 'no-inherit'");
643         }
644         nextToken();
645     }
646
647
648     /**
649      * Parse the "declare construction" declaration.
650      * Syntax: <"declare" "construction"> ("preserve" | "strip")
651      * @throws net.sf.saxon.trans.StaticError
652      */

653
654     private void parseConstructionDeclaration() throws StaticError {
655         if (foundConstructionDeclaration) {
656             grumble("declare construction appears more than once", "XQST0067");
657         }
658         foundConstructionDeclaration = true;
659         nextToken();
660         expect(Token.NAME);
661         int val;
662         if ("preserve".equals(t.currentTokenValue)) {
663             val = Validation.PRESERVE;
664         } else if ("strip".equals(t.currentTokenValue)) {
665             val = Validation.STRIP;
666         } else {
667             grumble("construction mode must be 'preserve' or 'strip'");
668             val = Validation.STRIP;
669         }
670         ((StaticQueryContext) env).setConstructionMode(val);
671         nextToken();
672     }
673
674     /**
675      * Parse (and process) the schema import declaration.
676      * SchemaImport ::= "import" "schema" SchemaPrefix? URILiteral ("at" URILiteral ("," URILiteral)*)?
677      * SchemaPrefix ::= ("namespace" NCName "=") | ("default" "element" "namespace")
678      */

679
680     private void parseSchemaImport() throws StaticError {
681         if (!env.getConfiguration().isSchemaAware(Configuration.XQUERY)) {
682             grumble("To import a schema, you need the schema-aware version of Saxon", "XQST0009");
683         }
684         String JavaDoc prefix = null;
685         String JavaDoc namespaceURI = null;
686         List locationURIs = new ArrayList(5);
687         nextToken();
688         if (isKeyword("namespace")) {
689             nextToken();
690             expect(Token.NAME);
691             prefix = t.currentTokenValue;
692             checkProhibitedPrefixes(prefix);
693             nextToken();
694             expect(Token.EQUALS);
695             nextToken();
696         } else if (isKeyword("default")) {
697             nextToken();
698             if (!isKeyword("element")) {
699                 grumble("In 'import schema', expected 'element namespace'");
700             }
701             nextToken();
702             if (!isKeyword("namespace")) {
703                 grumble("In 'import schema', expected keyword 'namespace'");
704             }
705             nextToken();
706             prefix = "";
707         }
708         if (t.currentToken == Token.STRING_LITERAL) {
709             namespaceURI = t.currentTokenValue;
710             nextToken();
711             if (isKeyword("at")) {
712                 nextToken();
713                 expect(Token.STRING_LITERAL);
714                 locationURIs.add(t.currentTokenValue);
715                 nextToken();
716                 while (t.currentToken == Token.COMMA) {
717                     nextToken();
718                     expect(Token.STRING_LITERAL);
719                     locationURIs.add(t.currentTokenValue);
720                     nextToken();
721                 }
722             } else if (t.currentToken != Token.SEMICOLON) {
723                 grumble("After the target namespace URI, expected 'at' or ';'");
724             }
725         } else {
726             grumble("After 'import schema', expected 'namespace', 'default', or a string-literal");
727         }
728         if (prefix != null) {
729             if (namespaceURI==null || "".equals(namespaceURI)) {
730                 grumble("A prefix cannot be bound to the null namespace", "XQST0057");
731             }
732             try {
733                 if ("".equals(prefix)) {
734                     ((StaticQueryContext) env).setDefaultElementNamespace(namespaceURI);
735                 } else {
736                     ((StaticQueryContext) env).declarePassiveNamespace(prefix, namespaceURI, true);
737                 }
738             } catch (StaticError err) {
739                 err.setLocator(makeLocator());
740                 reportError(err);
741             }
742         }
743
744         // Do the importing
745

746         Configuration config = env.getConfiguration();
747         if (config.getSchema(namespaceURI) == null) {
748             if (locationURIs.size() > 0) {
749                 try {
750                     PipelineConfiguration pipe = config.makePipelineConfiguration();
751                     config.readMultipleSchemas(pipe, env.getBaseURI(), locationURIs, namespaceURI);
752                 } catch (TransformerConfigurationException JavaDoc err) {
753                     grumble("Error in schema. " + err.getMessage(), "XQST0059");
754                 }
755             } else {
756                 grumble("Unable to locate requested schema", "XQST0059");
757             }
758         }
759         ((StaticQueryContext) env).addImportedSchema(namespaceURI);
760     }
761
762     /**
763      * Parse (and expand) the module import declaration.
764      * Syntax: <"import" "module" ("namespace" NCName "=")? uri ("at" uri ("," uri)*)? ";"
765      */

766
767     private void parseModuleImport() throws StaticError {
768         StaticQueryContext thisModule = (StaticQueryContext) env;
769         String JavaDoc prefix = null;
770         String JavaDoc moduleURI = null;
771         List locationURIs = new ArrayList(5);
772         nextToken();
773         if (t.currentToken == Token.NAME && t.currentTokenValue == "namespace") {
774             nextToken();
775             expect(Token.NAME);
776             prefix = t.currentTokenValue;
777             checkProhibitedPrefixes(prefix);
778             nextToken();
779             expect(Token.EQUALS);
780             nextToken();
781         }
782         if (t.currentToken == Token.STRING_LITERAL) {
783             moduleURI = t.currentTokenValue;
784             if (importedModules.contains(moduleURI)) {
785                 grumble("Two 'import module' declarations specify the same module namespace", "XQST0047");
786             }
787             importedModules.add(moduleURI);
788             ((StaticQueryContext)env).addImportedNamespace(moduleURI);
789             nextToken();
790             if (isKeyword("at")) {
791                 do {
792                     nextToken();
793                     expect(Token.STRING_LITERAL);
794                     String JavaDoc uri = t.currentTokenValue;
795                     try {
796                         uri = StandardURIResolver.makeAbsolute(uri, env.getBaseURI()).toString();
797                     } catch (DynamicError dynamicError) {
798                         grumble(dynamicError.getMessage());
799                     } catch (URISyntaxException JavaDoc e) {
800                         grumble("Invalid URI " + uri + ": " + e.getMessage());
801                     }
802                     locationURIs.add(uri);
803                     nextToken();
804                 } while (t.currentToken == Token.COMMA);
805             }
806         } else {
807             grumble("After 'import module', expected 'namespace' or a string-literal");
808         }
809         if (prefix != null) {
810             try {
811                 thisModule.declarePassiveNamespace(prefix, moduleURI, true);
812             } catch (StaticError err) {
813                 err.setLocator(makeLocator());
814                 reportError(err);
815             }
816         }
817         String JavaDoc thisModuleNS = thisModule.getModuleNamespace();
818
819         // Check that this import would not create a cycle involving a change of namespace
820
if (!moduleURI.equals(((StaticQueryContext)env).getModuleNamespace())) {
821             StaticQueryContext parent = (StaticQueryContext)env;
822             while (parent != null) {
823                 if (moduleURI.equals(parent.getModuleNamespace())) {
824                     StaticError err = new StaticError(
825                             "A module cannot import itself directly or indirectly, unless all modules in the cycle are in the same namespace");
826                     err.setErrorCode("XQST0073");
827                     throw err;
828                 }
829                 parent = parent.getImporter();
830             }
831         }
832
833         boolean foundOne = false;
834
835         // If any of the modules are already loaded, don't re-read them
836
List existingModules = executable.getQueryLibraryModules(moduleURI);
837         if (existingModules != null) {
838             for (int m=0; m<existingModules.size(); m++) {
839                 StaticQueryContext importedModule = (StaticQueryContext)existingModules.get(m);
840                 if (!importedModule.getLocationURI().equals(((StaticQueryContext)env).getLocationURI())) {
841                     //QueryReader.importModuleContents(importedModule, thisModule);
842
foundOne = true;
843                 }
844                 for (int h=locationURIs.size()-1; h>=0; h--) {
845                     if (locationURIs.get(h).equals(importedModule.getLocationURI())) {
846                         locationURIs.remove(h);
847                     }
848                 }
849             }
850         }
851
852         // If we've found at least one module, and there are no location URIs left, call it a day.
853

854         if (locationURIs.size() == 0 && foundOne) {
855             return;
856         }
857
858         // Call the module URI resolver to find the remaining modules
859

860         ModuleURIResolver resolver = env.getConfiguration().getModuleURIResolver();
861
862         String JavaDoc[] hints = new String JavaDoc[locationURIs.size()];
863         hints = (String JavaDoc[])locationURIs.toArray(hints);
864         StreamSource JavaDoc[] sources = null;
865         try {
866             if (resolver != null) {
867                 sources = resolver.resolve(moduleURI, env.getBaseURI(), hints);
868             }
869             if (sources == null) {
870                 if (hints.length == 0) {
871                     if (existingModules == null) {
872                         grumble("Cannot locate module for namespace " + moduleURI, "XQST0059");
873                     }
874                 }
875                 resolver = env.getConfiguration().getStandardModuleURIResolver();
876                 sources = resolver.resolve(moduleURI, env.getBaseURI(), hints);
877             }
878         } catch (XPathException e) {
879             throw StaticError.makeStaticError(e);
880         }
881
882         for (int m=0; m<sources.length; m++) {
883             StreamSource JavaDoc ss = sources[m];
884             String JavaDoc baseURI = ss.getSystemId();
885             if (baseURI == null) {
886                 if (m < hints.length) {
887                     ss.setSystemId(hints[m]);
888                 } else {
889                     grumble("No base URI available for imported module", "XQST0059");
890                 }
891             }
892             try {
893                 String JavaDoc queryText = QueryReader.readSourceQuery(ss);
894                 StaticQueryContext importedModule = StaticQueryContext.makeStaticQueryContext(
895                         baseURI, executable, thisModule, queryText, moduleURI);
896                 //QueryReader.importModuleContents(importedModule, thisModule);
897
} catch (StaticError err) {
898                 if (err.getLocator() == null) {
899                     err.setLocator(makeLocator());
900                 }
901                 reportError(err);
902             }
903         }
904     }
905
906     /**
907      * Parse the Base URI declaration.
908      * Syntax: <"declare" "base-uri"> uri-literal
909      * @throws net.sf.saxon.trans.StaticError
910      */

911
912     private void parseBaseURIDeclaration() throws StaticError {
913         if (foundBaseURIDeclaration) {
914             grumble("Base URI Declaration may only appear once", "XQST0032");
915         }
916         foundBaseURIDeclaration = true;
917         nextToken();
918         expect(Token.STRING_LITERAL);
919         String JavaDoc uri = t.currentTokenValue;
920         ((StaticQueryContext) env).setBaseURI(uri);
921         nextToken();
922     }
923
924     /**
925      * Parse the "default function namespace" declaration.
926      * Syntax: <"declare" "default" "function" "namespace"> StringLiteral
927      * @throws net.sf.saxon.trans.StaticError to indicate a syntax error
928      */

929
930     private void parseDefaultFunctionNamespace() throws StaticError {
931         if (foundDefaultFunctionNamespace) {
932             grumble("default function namespace appears more than once", "XQST0066");
933         }
934         foundDefaultFunctionNamespace = true;
935         nextToken();
936         expect(Token.NAME);
937         if (!"namespace".equals(t.currentTokenValue)) {
938             grumble("After 'declare default function', expected 'namespace'");
939         }
940         nextToken();
941         expect(Token.STRING_LITERAL);
942         String JavaDoc uri = t.currentTokenValue;
943         ((StaticQueryContext) env).setDefaultFunctionNamespace(uri);
944         nextToken();
945     }
946
947     /**
948      * Parse the "default element namespace" declaration.
949      * Syntax: <"declare" "default" "element" "namespace"> StringLiteral
950      * @throws net.sf.saxon.trans.StaticError to indicate a syntax error
951      */

952
953     private void parseDefaultElementNamespace() throws StaticError {
954         if (foundDefaultElementNamespace) {
955             grumble("default element namespace appears more than once", "XQST0066");
956         }
957         foundDefaultElementNamespace = true;
958         nextToken();
959         expect(Token.NAME);
960         if (!"namespace".equals(t.currentTokenValue)) {
961             grumble("After 'declare default element', expected 'namespace'");
962         }
963         nextToken();
964         expect(Token.STRING_LITERAL);
965         String JavaDoc uri = t.currentTokenValue;
966         ((StaticQueryContext) env).setDefaultElementNamespace(uri);
967         nextToken();
968     }
969
970     /**
971      * Parse a namespace declaration in the Prolog.
972      * Syntax: <"declare" "namespace"> NCName "=" StringLiteral
973      * @throws net.sf.saxon.trans.StaticError
974      */

975
976     private void parseNamespaceDeclaration() throws StaticError {
977         nextToken();
978         expect(Token.NAME);
979         String JavaDoc prefix = t.currentTokenValue;
980         if (!XMLChar.isValidNCName(prefix)) {
981             grumble("Invalid namespace prefix " + Err.wrap(prefix));
982         }
983         checkProhibitedPrefixes(prefix);
984         nextToken();
985         expect(Token.EQUALS);
986         nextToken();
987         expect(Token.STRING_LITERAL);
988         String JavaDoc uri = t.currentTokenValue;
989         if ("".equals(uri)) {
990             grumble("A namespace URI cannot be empty");
991         }
992         try {
993             ((StaticQueryContext) env).declarePassiveNamespace(prefix, uri, true);
994         } catch (StaticError err) {
995             err.setLocator(makeLocator());
996             reportError(err);
997         }
998         nextToken();
999     }
1000
1001    /**
1002     * Check that a namespace prefix is not a prohibited prefix (xml or xmlns)
1003     * @param prefix the prefix to be tested
1004     * @throws StaticError if the prefix is prohibited
1005     */

1006
1007    private void checkProhibitedPrefixes(String JavaDoc prefix) throws StaticError {
1008        if ("xml".equals(prefix) || "xmlns".equals(prefix)) {
1009            grumble("The namespace prefix " + Err.wrap(prefix) + " cannot be redeclared", "XQST0070");
1010        }
1011    }
1012
1013    /**
1014     * Parse a global variable definition.
1015     * <"declare" "variable" "$"> VarName TypeDeclaration?
1016     * ((":=" Expr ) | "external")
1017     * Currently accept both
1018     * @throws net.sf.saxon.trans.StaticError
1019     */

1020
1021    private void parseVariableDeclaration() throws StaticError {
1022        int offset = t.currentTokenStartOffset;
1023        GlobalVariableDefinition var = new GlobalVariableDefinition();
1024        var.setLineNumber(t.getLineNumber());
1025        var.setSystemId(env.getSystemId());
1026        nextToken();
1027        expect(Token.DOLLAR);
1028        t.setState(Tokenizer.BARE_NAME_STATE);
1029        nextToken();
1030        expect(Token.NAME);
1031        String JavaDoc varName = t.currentTokenValue;
1032        var.setVariableName(varName);
1033        int varNameCode = makeNameCode(t.currentTokenValue, false);
1034        int varFingerprint = varNameCode & 0xfffff;
1035        var.setNameCode(varFingerprint);
1036
1037        String JavaDoc uri = env.getNamePool().getURI(varNameCode);
1038        String JavaDoc moduleURI = ((StaticQueryContext)env).getModuleNamespace();
1039        if (moduleURI != null && !moduleURI.equals(uri)) {
1040            grumble("A variable declared in a library module must be in the module namespace", "XQST0048");
1041        }
1042
1043        nextToken();
1044        SequenceType requiredType = SequenceType.ANY_SEQUENCE;
1045        if (isKeyword("as")) {
1046            t.setState(Tokenizer.SEQUENCE_TYPE_STATE);
1047            nextToken();
1048            requiredType = parseSequenceType();
1049        }
1050        var.setRequiredType(requiredType);
1051
1052        if (t.currentToken == Token.ASSIGN) {
1053            t.setState(Tokenizer.DEFAULT_STATE);
1054            nextToken();
1055            Expression exp = parseExpression();
1056            var.setIsParameter(false);
1057            var.setValueExpression(makeTracer(offset, exp, StandardNames.XSL_VARIABLE, varNameCode));
1058        } else if (t.currentToken == Token.NAME) {
1059            if ("external".equals(t.currentTokenValue)) {
1060                var.setIsParameter(true);
1061                if (defaultValue != null) {
1062                    var.setValueExpression(defaultValue);
1063                }
1064                nextToken();
1065            } else {
1066                grumble("Variable must either be initialized or be declared as external");
1067            }
1068        } else {
1069            grumble("Expected ':=' or 'external' in variable declaration");
1070        }
1071
1072        StaticQueryContext qenv = (StaticQueryContext) env;
1073        if (qenv.getModuleNamespace() != null &&
1074                env.getNamePool().getURICode(varFingerprint) != qenv.getModuleNamespaceCode()) {
1075            grumble("Variable " + Err.wrap(varName, Err.VARIABLE) + " is not defined in the module namespace");
1076        }
1077        try {
1078            qenv.declareVariable(var);
1079        } catch (XPathException e) {
1080            grumble(e.getMessage(), e.getErrorCodeLocalPart());
1081        }
1082    }
1083
1084    /**
1085     * Parse a function declaration.
1086     * <p>Syntax:<br/>
1087     * <"declare" "function"> <QName "("> ParamList? (")" | (<")" "as"> SequenceType))
1088     * (EnclosedExpr | "external")
1089     * </p>
1090     * <p>On entry, the "define function" has already been recognized</p>
1091     * @throws net.sf.saxon.trans.StaticError if a syntax error is found
1092     */

1093
1094    private void parseFunctionDeclaration() throws StaticError {
1095        // the next token should be the < QNAME "("> pair
1096
int offset = t.currentTokenStartOffset;
1097        nextToken();
1098        expect(Token.FUNCTION);
1099
1100        String JavaDoc uri;
1101        int fnc;
1102        if (t.currentTokenValue.indexOf(':') < 0) {
1103            uri = env.getDefaultFunctionNamespace();
1104            fnc = env.getNamePool().allocate("", uri, t.currentTokenValue);
1105        } else {
1106            fnc = makeNameCode(t.currentTokenValue, false);
1107            uri = env.getNamePool().getURI(fnc);
1108        }
1109
1110        if (uri.equals("")) {
1111            grumble("The function must be in a namespace", "XQST0060");
1112        }
1113
1114        String JavaDoc moduleURI = ((StaticQueryContext)env).getModuleNamespace();
1115        if (moduleURI != null && !moduleURI.equals(uri)) {
1116            grumble("A function in a library module must be in the module namespace", "XQST0048");
1117        }
1118
1119        if (NamespaceConstant.isReservedInQuery(uri)) {
1120            grumble("The function name " + t.currentTokenValue + " is in a reserved namespace", "XQST0045");
1121        }
1122
1123        XQueryFunction func = new XQueryFunction();
1124        func.setNameCode(fnc);
1125        func.arguments = new ArrayList(8);
1126        func.resultType = SequenceType.ANY_SEQUENCE;
1127        func.body = null;
1128        func.lineNumber = t.getLineNumber(offset);
1129        func.columnNumber = t.getColumnNumber(offset);
1130        func.systemId = env.getSystemId();
1131        func.setExecutable(getExecutable());
1132
1133
1134        nextToken();
1135        HashSet paramNames = new HashSet(8);
1136        while (t.currentToken != Token.RPAR) {
1137            // ParamList ::= Param ("," Param)*
1138
// Param ::= "$" VarName TypeDeclaration?
1139
expect(Token.DOLLAR);
1140            nextToken();
1141            expect(Token.NAME);
1142            String JavaDoc argName = t.currentTokenValue;
1143            int nameCode = makeNameCode(argName, false);
1144            int fingerprint = nameCode & 0xfffff;
1145            Integer JavaDoc f = new Integer JavaDoc(fingerprint);
1146            if (paramNames.contains(f)) {
1147                grumble("Duplicate parameter name " + Err.wrap(t.currentTokenValue, Err.VARIABLE), "XQST0039");
1148            }
1149            paramNames.add(f);
1150            SequenceType paramType = SequenceType.ANY_SEQUENCE;
1151            nextToken();
1152            if (t.currentToken == Token.NAME && "as".equals(t.currentTokenValue)) {
1153                nextToken();
1154                paramType = parseSequenceType();
1155            }
1156
1157            RangeVariableDeclaration arg = new RangeVariableDeclaration();
1158            arg.setNameCode(nameCode);
1159            arg.setRequiredType(paramType);
1160            arg.setVariableName(argName);
1161            func.arguments.add(arg);
1162            declareRangeVariable(arg);
1163            if (t.currentToken == Token.RPAR) {
1164                break;
1165            } else if (t.currentToken == Token.COMMA) {
1166                nextToken();
1167            } else {
1168                grumble("Expected ',' or ')' after function argument, found '" +
1169                        Token.tokens[t.currentToken] + '\'');
1170            }
1171        }
1172        t.setState(Tokenizer.BARE_NAME_STATE);
1173        nextToken();
1174        if (isKeyword("as")) {
1175            t.setState(Tokenizer.SEQUENCE_TYPE_STATE);
1176            nextToken();
1177            func.resultType = parseSequenceType();
1178        }
1179        if (isKeyword("external")) {
1180            grumble("Saxon does not allow external functions to be declared");
1181        } else {
1182            expect(Token.LCURLY);
1183            t.setState(Tokenizer.DEFAULT_STATE);
1184            nextToken();
1185            func.body = parseExpression();
1186            if (func.body instanceof ComputedExpression) {
1187                ((ComputedExpression)func.body).setParentExpression(func);
1188            }
1189            expect(Token.RCURLY);
1190            lookAhead(); // must be done manually after an RCURLY
1191
}
1192        UserFunctionParameter[] params = func.getParameterDefinitions();
1193        for (int i = 0; i < params.length; i++) {
1194            undeclareRangeVariable();
1195        }
1196        t.setState(Tokenizer.DEFAULT_STATE);
1197        nextToken();
1198
1199        StaticQueryContext qenv = (StaticQueryContext) env;
1200
1201        try {
1202            qenv.declareFunction(func);
1203        } catch (XPathException e) {
1204            grumble(e.getMessage());
1205        }
1206
1207    }
1208
1209    /**
1210     * Parse an option declaration.
1211     * <p>Syntax:<br/>
1212     * <"declare" "option"> QName "string-literal"
1213     * </p>
1214     * <p>On entry, the "declare option" has already been recognized</p>
1215     * @throws net.sf.saxon.trans.StaticError if a syntax error is found
1216     */

1217
1218    private void parseOptionDeclaration() throws StaticError {
1219        nextToken();
1220        expect(Token.NAME);
1221        int varNameCode = makeNameCode(t.currentTokenValue, false);
1222        String JavaDoc uri = env.getNamePool().getURI(varNameCode);
1223
1224        nextToken();
1225        expect(Token.STRING_LITERAL);
1226        String JavaDoc value = t.currentTokenValue;
1227
1228        if (uri.equals(NamespaceConstant.SAXON)) {
1229            String JavaDoc localName = env.getNamePool().getLocalName(varNameCode);
1230            if (localName.equals("output")) {
1231                setOutputProperty(value);
1232            } else if (localName.equals("default")) {
1233                defaultValue = setDefaultValue(value);
1234            } else {
1235                warning("Unknown Saxon option declaration: " + env.getNamePool().getDisplayName(varNameCode));
1236            }
1237        }
1238
1239        nextToken();
1240    }
1241
1242    /**
1243     * Handle a saxon:output option declaration. Format:
1244     * declare option saxon:output "indent = yes"
1245     */

1246
1247    private void setOutputProperty(String JavaDoc property) {
1248        int equals = property.indexOf("=");
1249        if (equals < 0) {
1250            badOutputProperty("no equals sign");
1251        } else if (equals == 0) {
1252            badOutputProperty("starts with '=");
1253        } else if (equals == property.length()-1) {
1254            badOutputProperty("ends with '=");
1255        }
1256        String JavaDoc keyword = property.substring(0, equals).trim();
1257        String JavaDoc value = property.substring(equals+1).trim();
1258        Properties props = getExecutable().getDefaultOutputProperties();
1259        try {
1260            ResultDocument.setSerializationProperty(
1261                    props,
1262                    makeNameCode(keyword, false) & NamePool.FP_MASK,
1263                    value,
1264                    env.getNamePool(),
1265                    env.getNamespaceResolver()
1266            );
1267        } catch (XPathException e) {
1268            badOutputProperty(e.getMessage());
1269        }
1270    }
1271
1272    private void badOutputProperty(String JavaDoc s) {
1273        try {
1274            warning("Invalid serialization property (" + s + ") - ignored");
1275        } catch (StaticError staticError) {
1276            //
1277
}
1278    }
1279
1280    /**
1281     * Parse the expression (inside a string literal) used to define default values
1282     * for external variables. This requires instantiating a nested XPath parser.
1283     */

1284
1285    public Expression setDefaultValue(String JavaDoc exp) {
1286        try {
1287            IndependentContext ic = new IndependentContext(env.getConfiguration());
1288            ic.setNamespaceResolver(env.getNamespaceResolver());
1289            Expression expr = ExpressionTool.make(exp, ic, 0, Token.EOF, 1);
1290
1291            ItemType contextItemType = Type.ITEM_TYPE;
1292            expr = expr.typeCheck(ic, contextItemType);
1293            expr = expr.optimize(env.getConfiguration().getOptimizer(), env, contextItemType);
1294            SlotManager stackFrameMap = ic.getStackFrameMap();
1295            ExpressionTool.allocateSlots(expr, stackFrameMap.getNumberOfVariables(), stackFrameMap);
1296            return expr;
1297        } catch (XPathException e) {
1298            try {
1299                warning("Invalid expression for default value: " + e.getMessage() + " (ignored)");
1300            } catch (StaticError staticError) {
1301                //
1302
}
1303            return null;
1304        }
1305    }
1306
1307    /**
1308     * Parse a FLWOR expression. This replaces the XPath "for" expression.
1309     * Full syntax:
1310     * <p>
1311     * [41] FLWORExpr ::= (ForClause | LetClause)+
1312     * WhereClause? OrderByClause?
1313     * "return" ExprSingle
1314     * [42] ForClause ::= <"for" "$"> VarName TypeDeclaration? PositionalVar? "in" ExprSingle
1315     * ("," "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle)*
1316     * [43] PositionalVar ::= "at" "$" VarName
1317     * [44] LetClause ::= <"let" "$"> VarName TypeDeclaration? ":=" ExprSingle
1318     * ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
1319     * [45] WhereClause ::= "where" Expr
1320     * [46] OrderByClause ::= (<"order" "by"> | <"stable" "order" "by">) OrderSpecList
1321     * [47] OrderSpecList ::= OrderSpec ("," OrderSpec)*
1322     * [48] OrderSpec ::= ExprSingle OrderModifier
1323     * [49] OrderModifier ::= ("ascending" | "descending")?
1324     * (<"empty" "greatest"> | <"empty" "least">)?
1325     * ("collation" StringLiteral)?
1326     * </p>
1327     * @exception net.sf.saxon.trans.StaticError if any error is encountered
1328     * @return the resulting subexpression
1329     */

1330
1331    protected Expression parseForExpression() throws StaticError {
1332        int offset = t.currentTokenStartOffset;
1333        Expression whereCondition = null;
1334        int whereOffset = -1;
1335        //boolean stableOrder = false;
1336
List clauseList = new ArrayList(4);
1337        while (true) {
1338            if (t.currentToken == Token.FOR) {
1339                parseForClause(clauseList);
1340            } else if (t.currentToken == Token.LET) {
1341                parseLetClause(clauseList);
1342            } else {
1343                break;
1344            }
1345        }
1346        if (t.currentToken == Token.WHERE || isKeyword("where")) {
1347            whereOffset = t.currentTokenStartOffset;
1348            nextToken();
1349            whereCondition = parseExpression();
1350        }
1351        int orderByOffset = t.currentTokenStartOffset;
1352        if (isKeyword("stable")) {
1353            // we read the "stable" keyword but ignore it; Saxon ordering is always stable
1354
nextToken();
1355            if (!isKeyword("order")) {
1356                grumble("'stable' must be followed by 'order by'");
1357            }
1358        }
1359        List sortSpecList = null;
1360        if (isKeyword("order")) {
1361            t.setState(Tokenizer.BARE_NAME_STATE);
1362            nextToken();
1363            if (!isKeyword("by")) {
1364                grumble("'order' must be followed by 'by'");
1365            }
1366            t.setState(Tokenizer.DEFAULT_STATE);
1367            nextToken();
1368            sortSpecList = parseSortDefinition();
1369        }
1370        int returnOffset = t.currentTokenStartOffset;
1371        expect(Token.RETURN);
1372        t.setState(Tokenizer.DEFAULT_STATE);
1373        nextToken();
1374        Expression action = parseExprSingle();
1375        action = makeTracer(returnOffset, action, Location.RETURN_EXPRESSION, -1);
1376
1377
1378
1379        // If there is an order by clause, we modify the "return" expression so that it
1380
// returns a tuple containing the actual return value, plus the value of
1381
// each of the sort keys. We then wrap the entire FLWR expression inside a
1382
// TupleSorter that sorts the stream of tuples according to the sort keys,
1383
// discarding the sort keys and returning only the true result. The tuple
1384
// is implemented as a Java object wrapped inside an ObjectValue, which is
1385
// a general-purpose wrapper for objects that don't fit in the XPath type system.
1386

1387        if (sortSpecList != null) {
1388            TupleExpression exp = new TupleExpression(1 + sortSpecList.size());
1389            setLocation(exp);
1390            exp.setExpression(0, action);
1391            for (int i = 0; i < sortSpecList.size(); i++) {
1392                try {
1393                    RoleLocator role = new RoleLocator(RoleLocator.ORDER_BY, "FLWR", i, null);
1394                    role.setSourceLocator(makeLocator());
1395                    Expression sk =
1396                            TypeChecker.staticTypeCheck(
1397                                    ((SortSpec) sortSpecList.get(i)).sortKey,
1398                                    SequenceType.OPTIONAL_ATOMIC,
1399                                    false,
1400                                    role, env);
1401                    exp.setExpression(i + 1, sk);
1402                } catch (XPathException err) {
1403                    grumble(err.getMessage());
1404                }
1405            }
1406            action = exp;
1407        }
1408
1409        // if there is a "where" condition, we implement this by wrapping an if/then/else
1410
// around the "return" expression. No clever optimization yet!
1411

1412        if (whereCondition != null) {
1413            action = new IfExpression(whereCondition, action, EmptySequence.getInstance());
1414            action = makeTracer(whereOffset, action, Location.WHERE_CLAUSE, -1);
1415            setLocation(action);
1416        }
1417
1418        for (int i = clauseList.size() - 1; i >= 0; i--) {
1419            Object JavaDoc clause = clauseList.get(i);
1420            if (clause instanceof ExpressionParser.ForClause) {
1421                ExpressionParser.ForClause fc = (ExpressionParser.ForClause) clause;
1422                ForExpression exp = new ForExpression();
1423                exp.setVariableDeclaration(fc.rangeVariable);
1424                exp.setPositionVariable(fc.positionVariable);
1425                exp.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), t.getLineNumber(fc.offset)));
1426                exp.setSequence(fc.sequence);
1427                exp.setAction(action);
1428                action = makeTracer(fc.offset, exp, Location.FOR_EXPRESSION, fc.rangeVariable.getNameCode());
1429            } else {
1430                LetClause lc = (LetClause) clause;
1431                LetExpression exp = makeLetExpression();
1432                exp.setVariableDeclaration(lc.variable);
1433                exp.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), t.getLineNumber(lc.offset)));
1434                exp.setSequence(lc.value);
1435                exp.setAction(action);
1436                action = makeTracer(lc.offset, exp, Location.LET_EXPRESSION, lc.variable.getNameCode());
1437            }
1438        }
1439
1440        // Now wrap the whole expression in a TupleSorter if there is a sort specification
1441

1442        if (sortSpecList != null) {
1443            FixedSortKeyDefinition[] keys = new FixedSortKeyDefinition[sortSpecList.size()];
1444            for (int i = 0; i < sortSpecList.size(); i++) {
1445                SortSpec spec = (SortSpec) sortSpecList.get(i);
1446                FixedSortKeyDefinition key = new FixedSortKeyDefinition();
1447                //key.setSortKey(((SortSpec) sortSpecList.get(i)).sortKey);
1448
key.setSortKey(StringValue.EMPTY_STRING);
1449                key.setOrder(new StringValue(spec.ascending ? "ascending" : "descending"));
1450                key.setEmptyFirst(spec.ascending ? spec.emptyLeast : !spec.emptyLeast);
1451                try {
1452                    if (spec.collation != null) {
1453                        key.setCollation(env.getCollation(spec.collation));
1454                    }
1455                    key.bindComparer(env.getConfiguration());
1456                    keys[i] = key;
1457                } catch (XPathException e) {
1458                    grumble(e.getMessage());
1459                }
1460            }
1461            TupleSorter sorter = new TupleSorter(action, keys);
1462            setLocation(sorter);
1463            action = makeTracer(orderByOffset, sorter, Location.ORDER_BY_CLAUSE, -1);
1464        }
1465
1466        // undeclare all the range variables
1467

1468        for (int i = clauseList.size() - 1; i >= 0; i--) {
1469            Object JavaDoc clause = clauseList.get(i);
1470            if ((clause instanceof ExpressionParser.ForClause) &&
1471                    ((ExpressionParser.ForClause) clause).positionVariable != null) {
1472                // undeclare the "at" variable if it was declared
1473
undeclareRangeVariable();
1474            }
1475            // undeclare the primary variable
1476
undeclareRangeVariable();
1477        }
1478
1479        setLocation(action, offset);
1480        return action;
1481
1482    }
1483
1484    /**
1485     * Make a LetExpression. This returns an ordinary LetExpression if tracing is off, and an EagerLetExpression
1486     * if tracing is on. This is so that trace events occur in an order that the user can follow.
1487     */

1488
1489    private LetExpression makeLetExpression() {
1490        if (env.getConfiguration().getTraceListener() == null) {
1491            return new LetExpression();
1492        } else {
1493            return new EagerLetExpression();
1494        }
1495    }
1496
1497    /**
1498     * Parse a ForClause.
1499     * <p>
1500     * [42] ForClause ::= <"for" "$"> VarName TypeDeclaration? PositionalVar? "in" ExprSingle
1501     * ("," "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle)*
1502     * </p>
1503     * @param clauseList - the components of the parsed ForClause are appended to the
1504     * supplied list
1505     * @throws net.sf.saxon.trans.StaticError
1506     */

1507    private void parseForClause(List clauseList) throws StaticError {
1508        boolean first = true;
1509        do {
1510            ExpressionParser.ForClause clause = new ExpressionParser.ForClause();
1511            if (first) {
1512                clause.offset = t.currentTokenStartOffset;
1513            }
1514            clauseList.add(clause);
1515            nextToken();
1516            if (first) {
1517                first = false;
1518            } else {
1519                clause.offset = t.currentTokenStartOffset;
1520            }
1521            expect(Token.DOLLAR);
1522            nextToken();
1523            expect(Token.NAME);
1524            String JavaDoc var = t.currentTokenValue;
1525
1526            RangeVariableDeclaration v = new RangeVariableDeclaration();
1527            v.setNameCode(makeNameCode(var, false));
1528            v.setRequiredType(SequenceType.SINGLE_ITEM);
1529            v.setVariableName(var);
1530            clause.rangeVariable = v;
1531            nextToken();
1532
1533            if (isKeyword("as")) {
1534                nextToken();
1535                SequenceType type = parseSequenceType();
1536                v.setRequiredType(type);
1537                if (type.getCardinality() != StaticProperty.EXACTLY_ONE) {
1538                    grumble("Cardinality of range variable must be exactly one");
1539                }
1540            }
1541            clause.positionVariable = null;
1542            if (isKeyword("at")) {
1543                nextToken();
1544                expect(Token.DOLLAR);
1545                nextToken();
1546                expect(Token.NAME);
1547                RangeVariableDeclaration pos = new RangeVariableDeclaration();
1548                pos.setNameCode(makeNameCode(t.currentTokenValue, false));
1549                pos.setRequiredType(SequenceType.SINGLE_INTEGER);
1550                pos.setVariableName(t.currentTokenValue);
1551                clause.positionVariable = pos;
1552                //declareRangeVariable(pos);
1553
nextToken();
1554            }
1555            expect(Token.IN);
1556            nextToken();
1557            clause.sequence = parseExprSingle();
1558            declareRangeVariable(clause.rangeVariable);
1559            if (clause.positionVariable != null) {
1560                declareRangeVariable(clause.positionVariable);
1561            }
1562        } while (t.currentToken == Token.COMMA);
1563    }
1564
1565    /**
1566     * Parse a LetClause.
1567     * <p>
1568     * [44] LetClause ::= <"let" "$"> VarName TypeDeclaration? ":=" ExprSingle
1569     * ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
1570     * </p>
1571     * @param clauseList - the components of the parsed LetClause are appended to the
1572     * supplied list
1573     * @throws net.sf.saxon.trans.StaticError
1574     */

1575    private void parseLetClause(List clauseList) throws StaticError {
1576        boolean first = true;
1577        do {
1578            LetClause clause = new LetClause();
1579            if (first) {
1580                clause.offset = t.currentTokenStartOffset;
1581            }
1582            clauseList.add(clause);
1583            nextToken();
1584            if (first) {
1585                first = false;
1586            } else {
1587                clause.offset = t.currentTokenStartOffset;
1588            }
1589            expect(Token.DOLLAR);
1590            nextToken();
1591            expect(Token.NAME);
1592            String JavaDoc var = t.currentTokenValue;
1593
1594            RangeVariableDeclaration v = new RangeVariableDeclaration();
1595            v.setNameCode(makeNameCode(var, false));
1596            v.setRequiredType(SequenceType.ANY_SEQUENCE);
1597            v.setVariableName(var);
1598            clause.variable = v;
1599            nextToken();
1600
1601            if (isKeyword("as")) {
1602                nextToken();
1603                v.setRequiredType(parseSequenceType());
1604            }
1605
1606            expect(Token.ASSIGN);
1607            nextToken();
1608            clause.value = parseExprSingle();
1609            declareRangeVariable(v);
1610        } while (t.currentToken == Token.COMMA);
1611    }
1612
1613    /**
1614    * Make a string-join expression that concatenates the string-values of items in
1615    * a sequence with intervening spaces. This may be simplified later as a result
1616    * of type-checking.
1617    */

1618
1619    public static Expression makeStringJoin(Expression exp, StaticContext env) {
1620
1621        exp = new Atomizer(exp, env.getConfiguration());
1622        ItemType t = exp.getItemType();
1623        if (t != Type.STRING_TYPE && t != Type.UNTYPED_ATOMIC_TYPE) {
1624            exp = new AtomicSequenceConverter(exp, Type.STRING_TYPE);
1625        }
1626
1627        StringJoin fn = (StringJoin)SystemFunction.makeSystemFunction("string-join", 2, env.getNamePool());
1628        Expression[] args = new Expression[2];
1629        args[0] = exp;
1630        args[1] = StringValue.SINGLE_SPACE;
1631        fn.setArguments(args);
1632        if (exp instanceof ComputedExpression) {
1633            fn.setLocationId(((ComputedExpression)exp).getLocationId());
1634        }
1635        return fn;
1636    }
1637
1638    private static class LetClause {
1639        public RangeVariableDeclaration variable;
1640        public Expression value;
1641        public int offset;
1642    }
1643
1644    /**
1645     * Parse the "order by" clause.
1646     * [46] OrderByClause ::= (<"order" "by"> | <"stable" "order" "by">) OrderSpecList
1647     * [47] OrderSpecList ::= OrderSpec ("," OrderSpec)*
1648     * [48] OrderSpec ::= ExprSingle OrderModifier
1649     * [49] OrderModifier ::= ("ascending" | "descending")?
1650     * (<"empty" "greatest"> | <"empty" "least">)?
1651     * ("collation" StringLiteral)?
1652     * @return a list of sort specifications (SortSpec), one per sort key
1653     * @throws net.sf.saxon.trans.StaticError
1654     */

1655    private List parseSortDefinition() throws StaticError {
1656        List sortSpecList = new ArrayList(5);
1657        while (true) {
1658            SortSpec sortSpec = new SortSpec();
1659            sortSpec.sortKey = parseExprSingle();
1660            sortSpec.ascending = true;
1661            sortSpec.emptyLeast = defaultEmptyLeast;
1662            sortSpec.collation = env.getDefaultCollationName();
1663            //t.setState(t.BARE_NAME_STATE);
1664
if (isKeyword("ascending")) {
1665                nextToken();
1666            } else if (isKeyword("descending")) {
1667                sortSpec.ascending = false;
1668                nextToken();
1669            }
1670            if (isKeyword("empty")) {
1671                nextToken();
1672                if (isKeyword("greatest")) {
1673                    sortSpec.emptyLeast = false;
1674                    nextToken();
1675                } else if (isKeyword("least")) {
1676                    sortSpec.emptyLeast = true;
1677                    nextToken();
1678                } else {
1679                    grumble("'empty' must be followed by 'greatest' or 'least'");
1680                }
1681            }
1682            if (isKeyword("collation")) {
1683                nextToken();
1684                expect(Token.STRING_LITERAL);
1685                sortSpec.collation = t.currentTokenValue;
1686                nextToken();
1687            }
1688            sortSpecList.add(sortSpec);
1689            if (t.currentToken == Token.COMMA) {
1690                nextToken();
1691            } else {
1692                break;
1693            }
1694        }
1695        return sortSpecList;
1696    }
1697
1698    private static class SortSpec {
1699        public Expression sortKey;
1700        public boolean ascending;
1701        public boolean emptyLeast;
1702        public String JavaDoc collation;
1703    }
1704
1705    /**
1706     * Parse a Typeswitch Expression.
1707     * This construct is XQuery-only.
1708     * TypeswitchExpr ::=
1709     * "typeswitch" "(" Expr ")"
1710     * CaseClause+
1711     * "default" ("$" VarName)? "return" ExprSingle
1712     * CaseClause ::=
1713     * "case" ("$" VarName "as")? SequenceType "return" Expr
1714     */

1715
1716    protected Expression parseTypeswitchExpression() throws StaticError {
1717
1718        // On entry, the "(" has already been read
1719
int offset = t.currentTokenStartOffset;
1720        nextToken();
1721        Expression operand = parseExpression();
1722        List types = new ArrayList(10);
1723        List actions = new ArrayList(10);
1724        expect(Token.RPAR);
1725        nextToken();
1726
1727        // The code generated takes the form:
1728
// let $zzz := operand return
1729
// if ($zzz instance of t1) then action1
1730
// else if ($zzz instance of t2) then action2
1731
// else default-action
1732
//
1733
// If a variable is declared in a case clause or default clause,
1734
// then "action-n" takes the form
1735
// let $v as type := $zzz return action-n
1736

1737        // we were generating "let $v as type := $zzz return action-n" but this gives a compile time error if
1738
// there's a case clause that specifies an impossible type.
1739

1740        LetExpression outerLet = makeLetExpression();
1741
1742        RangeVariableDeclaration gen = new RangeVariableDeclaration();
1743        gen.setNameCode(makeNameCode("zz_typeswitchVar", false));
1744        gen.setRequiredType(SequenceType.ANY_SEQUENCE);
1745        gen.setVariableName("zz_typeswitchVar");
1746
1747        outerLet.setVariableDeclaration(gen);
1748        outerLet.setSequence(operand);
1749
1750        while (t.currentToken == Token.CASE) {
1751            int caseOffset = t.currentTokenStartOffset;
1752            SequenceType type;
1753            Expression action;
1754            nextToken();
1755            if (t.currentToken == Token.DOLLAR) {
1756                nextToken();
1757                expect(Token.NAME);
1758                final String JavaDoc var = t.currentTokenValue;
1759                final int varCode = makeNameCode(var, false);
1760                nextToken();
1761                expect(Token.NAME);
1762                if (!"as".equals(t.currentTokenValue)) {
1763                    grumble("After 'case $" + var + "', expected 'as'");
1764                }
1765                nextToken();
1766                type = parseSequenceType();
1767                action = makeTracer(caseOffset,
1768                                    parseTypeswitchReturnClause(var, varCode, gen),
1769                                    Location.CASE_EXPRESSION,
1770                                    varCode);
1771                if (action instanceof TraceExpression) {
1772                    ((TraceExpression)action).setProperty("type", type.toString());
1773                }
1774
1775            } else {
1776                type = parseSequenceType();
1777                t.treatCurrentAsOperator();
1778                expect(Token.RETURN);
1779                nextToken();
1780                action = makeTracer(caseOffset, parseExprSingle(), Location.CASE_EXPRESSION, -1);
1781                if (action instanceof TraceExpression) {
1782                    ((TraceExpression)action).setProperty("type", type.toString());
1783                }
1784            }
1785            types.add(type);
1786            actions.add(action);
1787        }
1788        if (types.size() == 0) {
1789            grumble("At least one case clause is required in a typeswitch");
1790        }
1791        expect(Token.DEFAULT);
1792        final int defaultOffset = t.currentTokenStartOffset;
1793        nextToken();
1794        Expression defaultAction;
1795        if (t.currentToken == Token.DOLLAR) {
1796            nextToken();
1797            expect(Token.NAME);
1798            final String JavaDoc var = t.currentTokenValue;
1799            final int varCode = makeNameCode(var, false);
1800            nextToken();
1801            defaultAction = makeTracer(
1802                    defaultOffset,
1803                    parseTypeswitchReturnClause(var, varCode, gen),
1804                    Location.DEFAULT_EXPRESSION,
1805                    varCode);
1806        } else {
1807            t.treatCurrentAsOperator();
1808            expect(Token.RETURN);
1809            nextToken();
1810            defaultAction = makeTracer(defaultOffset, parseExprSingle(), Location.DEFAULT_EXPRESSION, -1);
1811        }
1812
1813        Expression lastAction = defaultAction;
1814        for (int i = types.size() - 1; i >= 0; i--) {
1815            final VariableReference var = new VariableReference(gen);
1816            setLocation(var);
1817            final InstanceOfExpression ioe =
1818                    new InstanceOfExpression(var, (SequenceType)types.get(i));
1819            setLocation(ioe);
1820            final IfExpression ife =
1821                    new IfExpression(ioe, (Expression) actions.get(i), lastAction);
1822            setLocation(ife);
1823            lastAction = ife;
1824        }
1825        outerLet.setAction(lastAction);
1826        return makeTracer(offset, outerLet, Location.TYPESWITCH_EXPRESSION, -1);
1827    }
1828
1829    private Expression parseTypeswitchReturnClause(String JavaDoc var, int varCode, RangeVariableDeclaration gen)
1830            throws StaticError {
1831        Expression action;
1832        t.treatCurrentAsOperator();
1833        expect(Token.RETURN);
1834        nextToken();
1835
1836        RangeVariableDeclaration v = new RangeVariableDeclaration();
1837        v.setNameCode(varCode);
1838        v.setRequiredType(SequenceType.ANY_SEQUENCE);
1839        v.setVariableName(var);
1840
1841        declareRangeVariable(v);
1842        action = parseExprSingle();
1843        undeclareRangeVariable();
1844
1845        LetExpression innerLet = makeLetExpression();
1846        innerLet.setVariableDeclaration(v);
1847        innerLet.setSequence(new VariableReference(gen));
1848        innerLet.setAction(action);
1849        action = innerLet;
1850        return action;
1851    }
1852
1853    /**
1854     * Parse a Validate Expression.
1855     * This construct is XQuery-only. The syntax allows:
1856     * validate mode? { Expr }
1857     * mode ::= "strict" | "lax"
1858     */

1859
1860    protected Expression parseValidateExpression() throws StaticError {
1861        if (!env.getConfiguration().isSchemaAware(Configuration.XQUERY)) {
1862            grumble("To use a validate expression, you need the schema-aware processor from http://www.saxonica.com/");
1863        }
1864        int offset = t.currentTokenStartOffset;
1865        int mode = Validation.STRICT;
1866        boolean foundCurly = false;
1867        switch (t.currentToken) {
1868            case Token.VALIDATE_STRICT:
1869                mode = Validation.STRICT;
1870                nextToken();
1871                break;
1872            case Token.VALIDATE_LAX:
1873                mode = Validation.LAX;
1874                nextToken();
1875                break;
1876            case Token.KEYWORD_CURLY:
1877                if (t.currentTokenValue=="validate") {
1878                    mode = Validation.STRICT;
1879                } else {
1880                    throw new AssertionError JavaDoc("shouldn't be parsing a validate expression");
1881                }
1882                foundCurly = true;
1883        }
1884
1885        if (!foundCurly) {
1886            expect(Token.LCURLY);
1887        }
1888        nextToken();
1889
1890        Expression exp = parseExpression();
1891        if (exp instanceof ElementCreator) {
1892            ((ElementCreator)exp).setValidationMode(mode);
1893        } else if (exp instanceof DocumentInstr) {
1894            ((DocumentInstr)exp).setValidationAction(mode);
1895        } else {
1896            // the expression must return a single element or document node. The type-
1897
// checking machinery can't handle a union type, so we just check that it's
1898
// a node for now. Because we are reusing XSLT copy-of code, we need
1899
// an ad-hoc check that the node is of the right kind.
1900
try {
1901                RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "validate", 0, null);
1902                role.setSourceLocator(makeLocator());
1903                exp = TypeChecker.staticTypeCheck(
1904                        exp,
1905                        SequenceType.SINGLE_NODE,
1906                        false,
1907                        role, env);
1908            } catch (XPathException err) {
1909                grumble(err.getMessage());
1910            }
1911            exp = new CopyOf(exp, true, mode, null, true);
1912            setLocation(exp);
1913            ((CopyOf)exp).setRequireDocumentOrElement(true);
1914        }
1915
1916        expect(Token.RCURLY);
1917        t.lookAhead(); // always done manually after an RCURLY
1918
nextToken();
1919        return makeTracer(offset, exp, Location.VALIDATE_EXPRESSION, -1);
1920    }
1921
1922    /**
1923     * Parse an Extension Expression.
1924     * Syntax: "(#" QName arbitrary-text "#)")+ "{" expr? "}"
1925     * Currenly Saxon does not recognize any extension expressions, so the pragma is ignored and
1926     * the following expression is returned. (We don't even check the QName)
1927     */

1928
1929    protected Expression parseExtensionExpression() throws StaticError {
1930        nextToken();
1931        if (t.currentToken == Token.PRAGMA) {
1932            return parseExtensionExpression();
1933        }
1934        expect(Token.LCURLY);
1935        nextToken();
1936        if (t.currentToken == Token.RCURLY) {
1937            t.lookAhead(); // always done manually after an RCURLY
1938
nextToken();
1939            grumble("Unrecognized pragma, with no fallback expression");
1940        }
1941        Expression expr = parseExpression();
1942        expect(Token.RCURLY);
1943        t.lookAhead(); // always done manually after an RCURLY
1944
nextToken();
1945        return expr;
1946    }
1947
1948    /**
1949     * Parse a node constructor. This is allowed only in XQuery. This method handles
1950     * both the XML-like "direct" constructors, and the XQuery-based "computed"
1951     * constructors.
1952     * @return an Expression for evaluating the parsed constructor
1953     * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
1954     */

1955
1956    protected Expression parseConstructor() throws StaticError {
1957        int offset = t.currentTokenStartOffset;
1958        switch (t.currentToken) {
1959            case Token.TAG:
1960                Expression tag = parsePseudoXML(false);
1961                lookAhead();
1962                t.setState(Tokenizer.OPERATOR_STATE);
1963                nextToken();
1964                return tag;
1965            case Token.KEYWORD_CURLY:
1966                String JavaDoc nodeKind = t.currentTokenValue;
1967                if (nodeKind == "validate") {
1968                    return parseValidateExpression();
1969                } else if (nodeKind == "ordered" || nodeKind=="unordered") {
1970                    // these are currently no-ops in Saxon
1971
nextToken();
1972                    Expression content = parseExpression();
1973                    expect(Token.RCURLY);
1974                    lookAhead(); // must be done manually after an RCURLY
1975
nextToken();
1976                    return content;
1977                } else if (nodeKind == "document") {
1978                    nextToken();
1979                    Expression content = parseExpression();
1980                    expect(Token.RCURLY);
1981                    lookAhead(); // must be done manually after an RCURLY
1982
nextToken();
1983                    DocumentInstr doc = new DocumentInstr(false, null, env.getBaseURI());
1984                    if (!((StaticQueryContext)env).isPreserveNamespaces()) {
1985                        content = new CopyOf(content, false, Validation.PRESERVE, null, true);
1986                    }
1987                    doc.setValidationAction(((StaticQueryContext)env).getConstructionMode());
1988                    doc.setContentExpression(content);
1989                    setLocation(doc, offset);
1990                    return doc;
1991
1992                } else if ("element".equals(nodeKind)) {
1993                    nextToken();
1994                    // get the expression that yields the element name
1995
Expression name = parseExpression();
1996                    expect(Token.RCURLY);
1997                    lookAhead(); // must be done manually after an RCURLY
1998
nextToken();
1999                    expect(Token.LCURLY);
2000                    t.setState(Tokenizer.DEFAULT_STATE);
2001                    nextToken();
2002                    Expression content = null;
2003                    if (t.currentToken != Token.RCURLY) {
2004                        // get the expression that yields the element content
2005
content = parseExpression();
2006                        // if the child expression creates another element,
2007
// suppress validation, as the parent already takes care of it
2008
if (content instanceof ElementCreator) {
2009                            ((ElementCreator) content).setValidationMode(Validation.PRESERVE);
2010                        }
2011                        expect(Token.RCURLY);
2012                    }
2013                    lookAhead(); // done manually after an RCURLY
2014
nextToken();
2015
2016                    Instruction inst;
2017                    if (name instanceof Value) {
2018                        // if element name is supplied as a literal, treat it like a direct element constructor
2019
int nameCode;
2020                        if (name instanceof StringValue) {
2021                            String JavaDoc lex = ((StringValue) name).getStringValue();
2022                            nameCode = makeNameCode(lex, true);
2023                        } else if (name instanceof QNameValue) {
2024                            nameCode = env.getNamePool().allocate("",
2025                                    ((QNameValue) name).getNamespaceURI(),
2026                                    ((QNameValue) name).getLocalName());
2027                        } else {
2028                            grumble("Element name must be either a string or a QName");
2029                            return null;
2030                        }
2031                        inst = new FixedElement(nameCode,
2032                                ((StaticQueryContext) env).getActiveNamespaceCodes(),
2033                                ((StaticQueryContext) env).isInheritNamespaces(),
2034                                null,
2035                                ((StaticQueryContext) env).getConstructionMode());
2036                        if (content == null) {
2037                            content = EmptySequence.getInstance();
2038                        }
2039                        ((FixedElement)inst).setContentExpression(content);
2040                        setLocation(inst, offset);
2041                        //makeContentConstructor(content, (InstructionWithChildren) inst, offset);
2042
return makeTracer(offset, inst,Location.LITERAL_RESULT_ELEMENT, nameCode);
2043                    } else {
2044                        // it really is a computed element constructor: save the namespace context
2045
inst = new ComputedElement(name, null,
2046                                env.getNamespaceResolver(),
2047                                null,
2048                                ((StaticQueryContext) env).getConstructionMode(),
2049                                ((StaticQueryContext) env).isInheritNamespaces(),
2050                                true);
2051                        setLocation(inst);
2052                        if (content == null) {
2053                            content = EmptySequence.getInstance();
2054                        }
2055                        ((ComputedElement)inst).setContentExpression(content);
2056                        setLocation(inst, offset);
2057                        //makeContentConstructor(content, (InstructionWithChildren) inst, offset);
2058
return makeTracer(offset, inst, StandardNames.XSL_ELEMENT, -1);
2059                    }
2060
2061
2062
2063                } else if ("attribute".equals(nodeKind)) {
2064                    nextToken();
2065                    Expression name = parseExpression();
2066                    expect(Token.RCURLY);
2067                    lookAhead(); // must be done manually after an RCURLY
2068
nextToken();
2069                    expect(Token.LCURLY);
2070                    t.setState(Tokenizer.DEFAULT_STATE);
2071                    nextToken();
2072                    Expression content = null;
2073                    if (t.currentToken != Token.RCURLY) {
2074                        content = parseExpression();
2075                        expect(Token.RCURLY);
2076                    }
2077                    lookAhead(); // after an RCURLY
2078
nextToken();
2079                    if (name instanceof StringValue && content instanceof Value) {
2080                        String JavaDoc attName = ((StringValue)name).getStringValue();
2081                        if (attName.equals("xmlns") || attName.startsWith("xmlns:")) {
2082                            grumble("Cannot create a namespace using an attribute constructor");
2083                        }
2084                        int nameCode = makeNameCode(attName, false);
2085                        FixedAttribute fatt = new FixedAttribute(nameCode,
2086                                Validation.STRIP,
2087                                null,
2088                                StandardNames.XDT_UNTYPED_ATOMIC);
2089                        fatt.setRejectDuplicates();
2090                        makeSimpleContent(content, fatt, offset);
2091                        return makeTracer(offset, fatt, StandardNames.XSL_ATTRIBUTE, -1);
2092                    } else {
2093                        Attribute att = new Attribute(
2094                                name,
2095                                null,
2096                                env.getNamespaceResolver(),
2097                                Validation.STRIP,
2098                                null,
2099                                -1,
2100                                true);
2101                        att.setRejectDuplicates();
2102                        makeSimpleContent(content, att, offset);
2103                        return makeTracer(offset, att, StandardNames.XSL_ATTRIBUTE, -1);
2104                    }
2105
2106                } else if ("text".equals(nodeKind)) {
2107                    nextToken();
2108                    if (t.currentToken == Token.RCURLY) {
2109                        lookAhead(); // after an RCURLY
2110
nextToken();
2111                        return EmptySequence.getInstance();
2112                    } else {
2113                        Expression value = parseExpression();
2114                        expect(Token.RCURLY);
2115                        lookAhead(); // after an RCURLY
2116
nextToken();
2117                        Expression select;
2118                        try {
2119                            select = stringify(value, true);
2120                        } catch (XPathException e) {
2121                            throw StaticError.makeStaticError(e);
2122                        }
2123                        ValueOf vof = new ValueOf(select, false, true);
2124                        setLocation(vof, offset);
2125                        return makeTracer(offset, vof, StandardNames.XSL_TEXT, -1);
2126                    }
2127                } else if ("comment".equals(nodeKind)) {
2128                    nextToken();
2129                    if (t.currentToken == Token.RCURLY) {
2130                        lookAhead(); // after an RCURLY
2131
nextToken();
2132                        return EmptySequence.getInstance();
2133                    } else {
2134                        Expression value = parseExpression();
2135                        expect(Token.RCURLY);
2136                        lookAhead(); // after an RCURLY
2137
nextToken();
2138                        Comment com = new Comment();
2139                        makeSimpleContent(value, com, offset);
2140                        return makeTracer(offset, com, StandardNames.XSL_COMMENT, -1);
2141                    }
2142                } else if ("processing-instruction".equals(nodeKind)) {
2143                    nextToken();
2144                    Expression name = parseExpression();
2145                    expect(Token.RCURLY);
2146                    lookAhead(); // must be done manually after an RCURLY
2147
nextToken();
2148                    expect(Token.LCURLY);
2149                    t.setState(Tokenizer.DEFAULT_STATE);
2150                    nextToken();
2151                    Expression content = null;
2152                    if (t.currentToken != Token.RCURLY) {
2153                        content = parseExpression();
2154                        expect(Token.RCURLY);
2155                    }
2156                    lookAhead(); // after an RCURLY
2157
nextToken();
2158                    ProcessingInstruction pi = new ProcessingInstruction(name);
2159                    makeSimpleContent(content, pi, offset);
2160                    return makeTracer(offset, pi, StandardNames.XSL_PROCESSING_INSTRUCTION, -1);
2161
2162                } else {
2163                    grumble("Unrecognized node constructor " + t.currentTokenValue + "{}");
2164
2165                }
2166            case Token.ELEMENT_QNAME:
2167                int nameCode = makeNameCode(t.currentTokenValue, true);
2168                Expression content = null;
2169                nextToken();
2170                if (t.currentToken != Token.RCURLY) {
2171                    content = parseExpression();
2172                    expect(Token.RCURLY);
2173                }
2174                lookAhead(); // after an RCURLY
2175
nextToken();
2176                FixedElement el2 = new FixedElement(nameCode,
2177                        ((StaticQueryContext) env).getActiveNamespaceCodes(),
2178                        ((StaticQueryContext) env).isInheritNamespaces(),
2179                        null,
2180                        ((StaticQueryContext) env).getConstructionMode());
2181                setLocation(el2, offset);
2182                if (content == null) {
2183                    content = EmptySequence.getInstance();
2184                }
2185                el2.setContentExpression(content);
2186                //makeContentConstructor(content, el2, offset);
2187
return makeTracer(offset, el2, Location.LITERAL_RESULT_ELEMENT, nameCode);
2188            case Token.ATTRIBUTE_QNAME:
2189                if (t.currentTokenValue.equals("xmlns") || t.currentTokenValue.startsWith("xmlns:")) {
2190                    grumble("Cannot create a namespace using an attribute constructor");
2191                }
2192                int attNameCode = makeNameCode(t.currentTokenValue, false);
2193                Expression attContent = null;
2194                nextToken();
2195                if (t.currentToken != Token.RCURLY) {
2196                    attContent = parseExpression();
2197                    expect(Token.RCURLY);
2198                }
2199                lookAhead(); // after an RCURLY
2200
nextToken();
2201                FixedAttribute att2 = new FixedAttribute(attNameCode,
2202                        Validation.STRIP,
2203                        null,
2204                        StandardNames.XDT_UNTYPED_ATOMIC);
2205                att2.setRejectDuplicates();
2206                makeSimpleContent(attContent, att2, offset);
2207                return makeTracer(offset, att2, Location.LITERAL_RESULT_ATTRIBUTE, attNameCode);
2208            case Token.PI_QNAME:
2209                Expression piName = new StringValue(t.currentTokenValue);
2210                Expression piContent = null;
2211                nextToken();
2212                if (t.currentToken != Token.RCURLY) {
2213                    piContent = parseExpression();
2214                    expect(Token.RCURLY);
2215                }
2216                lookAhead(); // after an RCURLY
2217
nextToken();
2218                ProcessingInstruction pi2 = new ProcessingInstruction(piName);
2219                makeSimpleContent(piContent, pi2, offset);
2220                return makeTracer(offset, pi2, StandardNames.XSL_PROCESSING_INSTRUCTION, -1);
2221        }
2222        return null;
2223    }
2224
2225    /**
2226     * Make the instructions for the children of a node with simple content (attribute, text, PI, etc)
2227     * @param content
2228     * @param inst
2229     * @param offset
2230     */

2231
2232    private void makeSimpleContent(Expression content, SimpleNodeConstructor inst, int offset) throws StaticError {
2233        try {
2234            if (content == null) {
2235                inst.setSelect(StringValue.EMPTY_STRING);
2236            } else {
2237                inst.setSelect(stringify(content, false));
2238            }
2239            setLocation(inst, offset);
2240        } catch (XPathException e) {
2241            grumble(e.getMessage());
2242        }
2243    }
2244
2245    /**
2246     * Make a sequence of instructions as the children of an element-construction instruction.
2247     * The idea here is to convert an XPath expression that "pulls" the content into a sequence
2248     * of XSLT-style instructions that push nodes directly onto the subtree, thus reducing the
2249     * need for copying of intermediate nodes.
2250     * @param content The content of the element as an expression
2251     * @param inst The element-construction instruction (Element or FixedElement)
2252     * @param offset the character position in the query
2253     */

2254// private void makeContentConstructor(Expression content, InstructionWithChildren inst, int offset) {
2255
// if (content == null) {
2256
// inst.setChildren(null);
2257
// } else if (content instanceof AppendExpression) {
2258
// List instructions = new ArrayList(10);
2259
// convertAppendExpression((AppendExpression) content, instructions);
2260
// inst.setChildren((Expression[]) instructions.toArray(new Expression[instructions.size()]));
2261
// } else {
2262
// Expression children[] = {content};
2263
// inst.setChildren(children);
2264
// }
2265
// setLocation(inst, offset);
2266
// }
2267

2268    /**
2269     * Parse pseudo-XML syntax in direct element constructors, comments, CDATA, etc.
2270     * This is handled by reading single characters from the Tokenizer until the
2271     * end of the tag (or an enclosed expression) is enountered.
2272     * This method is also used to read an end tag. Because an end tag is not an
2273     * expression, the method in this case returns a StringValue containing the
2274     * contents of the end tag.
2275     * @param allowEndTag true if the context allows an End Tag to appear here
2276     * @return an Expression representing the result of parsing the constructor.
2277     * If an end tag was read, its contents will be returned as a StringValue.
2278     */

2279
2280    private Expression parsePseudoXML(boolean allowEndTag) throws StaticError {
2281        Expression exp = null;
2282        int offset = t.inputOffset;
2283            // we're reading raw characters, so we don't want the currentTokenStartOffset
2284
char c = t.nextChar();
2285        switch (c) {
2286            case '!':
2287                c = t.nextChar();
2288                if (c == '-') {
2289                    exp = parseCommentConstructor();
2290                } else if (c == '[') {
2291                    grumble("A CDATA section is allowed only in element content");
2292                    // if CDATA were allowed here, we would have already read it
2293
} else {
2294                    grumble("Expected '--' or '[CDATA[' after '<!'");
2295                }
2296                break;
2297            case '?':
2298                exp = parsePIConstructor();
2299                break;
2300            case '/':
2301                if (allowEndTag) {
2302                    FastStringBuffer sb = new FastStringBuffer(40);
2303                    while (true) {
2304                        c = t.nextChar();
2305                        if (c == '>') break;
2306                        sb.append(c);
2307                    }
2308                    return new StringValue(sb);
2309                }
2310                grumble("Unmatched XML end tag");
2311                break;
2312            default:
2313                t.unreadChar();
2314                exp = parseDirectElementConstructor();
2315        }
2316        setLocation(exp, offset);
2317        return exp;
2318    }
2319
2320    /**
2321     * Parse a direct element constructor
2322     * @return the expression representing the constructor
2323     * @throws StaticError
2324     */

2325
2326    private Expression parseDirectElementConstructor() throws StaticError {
2327        int offset = t.inputOffset - 1;
2328            // we're reading raw characters, so we don't want the currentTokenStartOffset
2329
char c;
2330        FastStringBuffer buff = new FastStringBuffer(40);
2331        int namespaceCount = 0;
2332        while (true) {
2333            c = t.nextChar();
2334            if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '/' || c == '>') {
2335                break;
2336            }
2337            buff.append(c);
2338        }
2339        String JavaDoc elname = buff.toString();
2340        HashMap attributes = new HashMap(10);
2341        while (true) {
2342            // loop through the attributes
2343
// We must process namespace declaration attributes first;
2344
// their scope applies to all preceding attribute names and values.
2345
// But finding the delimiting quote of an attribute value requires the
2346
// XPath expressions to be parsed, because they may contain nested quotes.
2347
// So we parse in "scanOnly" mode, which ignores any undeclared namespace
2348
// prefixes, use the result of this parse to determine the length of the
2349
// attribute value, save the value, and reparse it when all the namespace
2350
// declarations have been dealt with.
2351
c = skipSpaces(c);
2352            if (c == '/' || c == '>') {
2353                break;
2354            }
2355            int attOffset = t.inputOffset - 1;
2356            buff.setLength(0);
2357            // read the attribute name
2358
while (true) {
2359                buff.append(c);
2360                c = t.nextChar();
2361                if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '=') {
2362                    break;
2363                }
2364            }
2365            String JavaDoc attName = buff.toString();
2366            if (!Name.isQName(attName)) {
2367                grumble("Invalid attribute name " + Err.wrap(attName, Err.ATTRIBUTE));
2368            }
2369            c = skipSpaces(c);
2370            expectChar(c, '=');
2371            c = t.nextChar();
2372            c = skipSpaces(c);
2373
2374            Expression avt;
2375            try {
2376                avt = makeAttributeContent(t.input, t.inputOffset, c, true);
2377            } catch (StaticError err) {
2378                grumble(err.getMessage());
2379                return null;
2380            } catch (XPathException err) {
2381                throw err.makeStatic();
2382            }
2383            // by convention, this returns the end position when called with scanOnly set
2384
int end = (int) ((IntegerValue) avt).longValue();
2385            // save the value with its surrounding quotes
2386
String JavaDoc val = t.input.substring(t.inputOffset-1, end+1);
2387            // and without
2388
String JavaDoc rval = t.input.substring(t.inputOffset, end);
2389            t.inputOffset = end + 1;
2390            // on return, the current character is the closing quote
2391
c = t.nextChar();
2392            if (!(c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '/' || c == '>')) {
2393                grumble("There must be whitespace after every attribute except the last");
2394            }
2395            if ("xmlns".equals(attName) || attName.startsWith("xmlns:")) {
2396                if (rval.indexOf('{') >= 0) {
2397                    grumble("Namespace URI must be a constant value", "XQST0022");
2398                }
2399                String JavaDoc prefix, uri;
2400                if ("xmlns".equals(attName)) {
2401                    prefix = "";
2402                    uri = rval;
2403                } else {
2404                    prefix = attName.substring(6);
2405                    if (prefix.equals("xml")) {
2406                        grumble("Cannot redeclare the XML namespace", "XQST0070");
2407                    } else if (prefix.equals("xmlns")) {
2408                        grumble("Cannot use xmlns as a namespace prefix", "XQST0070");
2409                    }
2410                    uri = rval;
2411                    if ("".equals(uri)) {
2412                        grumble("Namespace URI must not be empty", "XQST0085");
2413                    }
2414                }
2415                namespaceCount++;
2416                ((StaticQueryContext) env).declareActiveNamespace(prefix, uri);
2417            }
2418            if (attributes.get(attName) != null) {
2419                if ("xmlns".equals(attName) || attName.startsWith("xmlns:")) {
2420                    grumble("Duplicate namespace declaration " + attName, "XQST0071");
2421                } else {
2422                    grumble("Duplicate attribute name " + attName, "XQST0040");
2423                }
2424            }
2425            if (attName.equals("xml:id") && !XMLChar.isValidNCName(val)) {
2426                grumble("Value of xml:id must be a valid NCName", "XQST0082");
2427            }
2428            AttributeDetails a = new AttributeDetails();
2429            a.value = val;
2430            a.startOffset = attOffset;
2431            attributes.put(attName, a);
2432        }
2433        String JavaDoc namespace;
2434        int elNameCode = 0;
2435        try {
2436            String JavaDoc[] parts = Name.getQNameParts(elname);
2437            namespace = ((StaticQueryContext) env).checkURIForPrefix(parts[0]);
2438            if (namespace == null) {
2439                grumble("Undeclared prefix in element name " + Err.wrap(elname, Err.ELEMENT));
2440            }
2441            elNameCode = env.getNamePool().allocate(parts[0], namespace, parts[1]);
2442        } catch (QNameException e) {
2443            grumble("Invalid element name " + Err.wrap(elname, Err.ELEMENT));
2444        }
2445        int validationMode = ((StaticQueryContext) env).getConstructionMode();
2446        FixedElement elInst = new FixedElement(
2447                elNameCode,
2448                ((StaticQueryContext) env).getActiveNamespaceCodes(),
2449                ((StaticQueryContext) env).isInheritNamespaces(),
2450                null,
2451                validationMode);
2452
2453        setLocation(elInst, offset);
2454
2455        List contents = new ArrayList(10);
2456
2457        HashSet attFingerprints = new HashSet(attributes.size());
2458                    // we've checked for duplicate lexical QNames, but not for duplicate expanded-QNames
2459
for (Iterator iter = attributes.keySet().iterator(); iter.hasNext();) {
2460            String JavaDoc attName = (String JavaDoc) iter.next();
2461            AttributeDetails a = (AttributeDetails) attributes.get(attName);
2462            String JavaDoc attValue = a.value;
2463            int attOffset = a.startOffset;
2464
2465            if ("xmlns".equals(attName) || attName.startsWith("xmlns:")) {
2466                // do nothing
2467
} else if (scanOnly) {
2468                // This means we are prescanning an attribute constructor, and we found a nested attribute
2469
// constructor, which we have prescanned; we now don't want to re-process the nested attribute
2470
// constructor because it might depend on things like variables declared in the containing
2471
// attribute constructor, and in any case we're going to come back to it again later.
2472
// See test qxmp180
2473
} else {
2474                int attNameCode = 0;
2475                String JavaDoc attNamespace;
2476                try {
2477                    String JavaDoc[] parts = Name.getQNameParts(attName);
2478                    if ("".equals(parts[0])) {
2479                        // attributes don't use the default namespace
2480
attNamespace = "";
2481                    } else {
2482                        attNamespace = ((StaticQueryContext) env).checkURIForPrefix(parts[0]);
2483                    }
2484                    if (attNamespace == null) {
2485                        grumble("Undeclared prefix in attribute name " + Err.wrap(attName, Err.ATTRIBUTE));
2486                    }
2487                    attNameCode = env.getNamePool().allocate(parts[0], attNamespace, parts[1]);
2488                    Integer JavaDoc key = new Integer JavaDoc(attNameCode & NamePool.FP_MASK);
2489                    if (attFingerprints.contains(key)) {
2490                        grumble("Duplicate expanded attribute name " + attName, "XQST0040");
2491                    }
2492                    attFingerprints.add(key);
2493                } catch (QNameException e) {
2494                    grumble("Invalid attribute name " + Err.wrap(attName, Err.ATTRIBUTE));
2495                }
2496
2497                FixedAttribute attInst =
2498                        new FixedAttribute(attNameCode, Validation.STRIP, null, StandardNames.XDT_UNTYPED_ATOMIC);
2499
2500                setLocation(attInst);
2501                Expression select;
2502                try {
2503                    select = makeAttributeContent(attValue, 1, attValue.charAt(0), false);
2504                } catch (XPathException err) {
2505                    throw err.makeStatic();
2506                }
2507                attInst.setSelect(select);
2508                attInst.setRejectDuplicates();
2509                setLocation(attInst);
2510                contents.add(makeTracer(attOffset, attInst, Location.LITERAL_RESULT_ATTRIBUTE, attNameCode));
2511            }
2512        }
2513        if (c == '/') {
2514            // empty element tag
2515
expectChar(t.nextChar(), '>');
2516        } else {
2517            readElementContent(elname, contents);
2518        }
2519
2520        Expression[] elk = new Expression[contents.size()];
2521        for (int i = 0; i < contents.size(); i++) {
2522            // if the child expression creates another element,
2523
// suppress validation, as the parent already takes care of it
2524
// TODO: optimize this down through conditional expressions, FLWR expressions, etc
2525
if (validationMode != Validation.STRIP &&
2526                    contents.get(i) instanceof ElementCreator &&
2527                    ((ElementCreator) contents.get(i)).getValidationMode() == validationMode) {
2528                ((ElementCreator) contents.get(i)).setValidationMode(Validation.PRESERVE);
2529            }
2530            elk[i] = (Expression) contents.get(i);
2531        }
2532        Block block = new Block();
2533        block.setChildren(elk);
2534        elInst.setContentExpression(block);
2535
2536        // reset the in-scope namespaces to what they were before
2537

2538        for (int n = 0; n < namespaceCount; n++) {
2539            ((StaticQueryContext) env).undeclareNamespace();
2540        }
2541
2542        return makeTracer(offset, elInst, Location.LITERAL_RESULT_ELEMENT, elNameCode);
2543    }
2544
2545    /**
2546     * Parse the content of an attribute in a direct element constructor. This may contain nested expressions
2547     * within curly braces. A particular problem is that the namespaces used in the expression may not yet be
2548     * known. This means we need the ability to parse in "scanOnly" mode, where undeclared namespace prefixes
2549     * are ignored.
2550     *
2551     * The code is based on the XSLT code in {@link AttributeValueTemplate#make}: the main difference is that
2552     * character entities and built-in entity references need to be recognized and expanded. Also, whitespace
2553     * needs to be normalized, mimicking the action of an XML parser
2554    */

2555
2556    private Expression makeAttributeContent(String JavaDoc avt,
2557                                  int start,
2558                                  char terminator,
2559                                  boolean scanOnly) throws XPathException {
2560
2561        int lineNumber = t.getLineNumber();
2562        List components = new ArrayList(10);
2563
2564        int i0, i1, i2, i8, i9, len, last;
2565        last = start;
2566        len = avt.length();
2567        while (last < len) {
2568            i2 = avt.indexOf(terminator, last);
2569            if (i2 < 0) {
2570                throw new StaticError("Attribute constructor is not properly terminated");
2571            }
2572
2573            i0 = avt.indexOf("{", last);
2574            i1 = avt.indexOf("{{", last);
2575            i8 = avt.indexOf("}", last);
2576            i9 = avt.indexOf("}}", last);
2577
2578            if ((i0 < 0 || i2 < i0) && (i8 < 0 || i2 < i8)) { // found end of string
2579
addStringComponent(components, avt, last, i2);
2580
2581                // look for doubled quotes, and skip them (for now)
2582
if (i2+1 < avt.length() && avt.charAt(i2+1)==terminator) {
2583                    components.add(new StringValue(terminator + ""));
2584                    last = i2+2;
2585                    continue;
2586                } else {
2587                    last = i2;
2588                    break;
2589                }
2590            } else if (i8 >= 0 && (i0 < 0 || i8 < i0)) { // found a "}"
2591
if (i8 != i9) { // a "}" that isn't a "}}"
2592
throw new StaticError(
2593                            "Closing curly brace in attribute value template \"" + avt + "\" must be doubled");
2594                }
2595                addStringComponent(components, avt, last, i8 + 1);
2596                last = i8 + 2;
2597            } else if (i1 >= 0 && i1 == i0) { // found a doubled "{{"
2598
addStringComponent(components, avt, last, i1 + 1);
2599                last = i1 + 2;
2600            } else if (i0 >= 0) { // found a single "{"
2601
if (i0 > last) {
2602                    addStringComponent(components, avt, last, i0);
2603                }
2604                Expression exp;
2605                ExpressionParser parser;
2606                parser = new QueryParser();
2607                parser.setScanOnly(scanOnly);
2608                if (rangeVariables != null) {
2609                    parser.setRangeVariableStack(rangeVariables);
2610                }
2611                exp = parser.parse(avt, i0 + 1, Token.RCURLY, lineNumber, env);
2612                if (!scanOnly) {
2613                    exp = exp.simplify(env);
2614                }
2615                last = parser.getTokenizer().currentTokenStartOffset + 1;
2616                components.add(makeStringJoin(exp, env));
2617
2618            } else {
2619                throw new IllegalStateException JavaDoc("Internal error parsing direct attribute constructor");
2620            }
2621        }
2622
2623        // if this is simply a prescan, return the position of the end of the
2624
// AVT, so we can parse it properly later
2625

2626        if (scanOnly) {
2627            return new IntegerValue(last);
2628        }
2629
2630        // is it empty?
2631

2632        if (components.size() == 0) {
2633            return StringValue.EMPTY_STRING;
2634        }
2635
2636        // is it a single component?
2637

2638        if (components.size() == 1) {
2639            return ((Expression) components.get(0)).simplify(env);
2640        }
2641
2642        // otherwise, return an expression that concatenates the components
2643

2644        Concat fn = (Concat) SystemFunction.makeSystemFunction("concat", components.size(), env.getNamePool());
2645        Expression[] args = new Expression[components.size()];
2646        components.toArray(args);
2647        fn.setArguments(args);
2648        fn.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), lineNumber));
2649        return fn.simplify(env);
2650
2651    }
2652
2653    private void addStringComponent(List components, String JavaDoc avt, int start, int end)
2654    throws XPathException {
2655        // analyze fixed text within the value of a direct attribute constructor.
2656
if (start < end) {
2657            FastStringBuffer sb = new FastStringBuffer(end - start);
2658            for (int i = start; i < end; i++) {
2659                char c = avt.charAt(i);
2660                switch (c) {
2661                    case '&': {
2662                        int semic = avt.indexOf(';', i);
2663                        if (semic < 0) {
2664                            grumble("No closing ';' found for entity or character reference");
2665                        } else {
2666                            String JavaDoc entity = avt.substring(i + 1, semic);
2667                            sb.append(analyzeEntityReference(entity));
2668                            i = semic;
2669                        }
2670                        break;
2671                    }
2672                    case '<':
2673                        grumble("The < character must not appear in attribute content");
2674                        break;
2675                    case '\n':
2676                    case '\t':
2677                        sb.append(' ');
2678                        break;
2679                    case '\r':
2680                        sb.append(' ');
2681                        if (i+1 < end && avt.charAt(i+1) == '\n') {
2682                            i++;
2683                        }
2684                        break;
2685                    default:
2686                        sb.append(c);
2687
2688                }
2689            }
2690            components.add(StringValue.makeStringValue(sb));
2691        }
2692    }
2693
2694    /**
2695     * Read the content of a direct element constructor
2696     * @param startTag the element start tag
2697     * @param components an empty list, to which the expressions comprising the element contents are added
2698     * @throws StaticError if any static errors are detected
2699     */

2700    private void readElementContent(String JavaDoc startTag, List components) throws StaticError {
2701        try {
2702            boolean afterRightCurly = false;
2703            boolean afterEnclosedExpr = false;
2704            while (true) {
2705                // read all the components of the element value
2706
FastStringBuffer text = new FastStringBuffer(256);
2707                char c;
2708                boolean containsEntities = false;
2709                while (true) {
2710                    c = t.nextChar();
2711                    if (c == '<') {
2712                        // See if we've got a CDATA section
2713
if (t.nextChar() == '!') {
2714                            if (t.nextChar() == '[') {
2715                                readCDATASection(text);
2716                                containsEntities = true;
2717                                continue;
2718                            } else {
2719                                t.unreadChar();
2720                                t.unreadChar();
2721                            }
2722                        } else {
2723                            t.unreadChar();
2724                        }
2725                        break;
2726                    } else if (c == '&') {
2727                        text.append(readEntityReference());
2728                        containsEntities = true;
2729                    } else if (c == '}') {
2730                        c = t.nextChar();
2731                        if (c != '}') {
2732                            grumble("'}' must be written as '}}' within element content");
2733                        }
2734                        text.append(c);
2735                    } else if (c == '{') {
2736                        c = t.nextChar();
2737                        if (c != '{') {
2738                            c = '{';
2739                            break;
2740                        }
2741                        text.append(c);
2742                    } else {
2743                        text.append(c);
2744                    }
2745                    afterRightCurly = false;
2746                }
2747                if (text.length() > 0 &&
2748                        (containsEntities | preserveSpace | !Whitespace.isWhite(text))) {
2749                    ValueOf inst = new ValueOf(new StringValue(text.condense()), false, false);
2750                    setLocation(inst);
2751                    components.add(inst);
2752                    afterEnclosedExpr = false;
2753                }
2754                if (c == '<') {
2755                    Expression exp = parsePseudoXML(true);
2756                    // An end tag can appear here, and is returned as a string value
2757
if (exp instanceof StringValue) {
2758                        String JavaDoc endTag = ((StringValue) exp).getStringValue().trim();
2759                        if (endTag.equals(startTag)) {
2760                            return;
2761                        } else {
2762                            grumble("end tag </" + endTag +
2763                                    "> does not match start tag <" + startTag + '>');
2764                        }
2765                    } else {
2766                        components.add(exp);
2767                    }
2768                    afterRightCurly = false;
2769                } else {
2770                    // we read an '{' indicating an enclosed expression
2771
if (afterEnclosedExpr) {
2772                        Expression previousComponent = (Expression)components.get(components.size()-1);
2773                        ItemType previousItemType = previousComponent.getItemType();
2774                        if (!(previousItemType instanceof NodeTest)) {
2775                            // Add a zero-length text node, to prevent {"a"}{"b"} generating an intervening space
2776
// See tests (qxmp132, qxmp261)
2777
ValueOf inst = new ValueOf(StringValue.EMPTY_STRING, false, false);
2778                            setLocation(inst);
2779                            components.add(inst);
2780                        }
2781                    }
2782                    t.unreadChar();
2783                    t.setState(Tokenizer.DEFAULT_STATE);
2784                    lookAhead();
2785                    nextToken();
2786                    Expression exp = parseExpression();
2787                    if (!((StaticQueryContext)env).isPreserveNamespaces()) {
2788                        exp = new CopyOf(exp, false, Validation.PRESERVE, null, true);
2789                    }
2790                    components.add(exp);
2791                    expect(Token.RCURLY);
2792                    afterRightCurly = true;
2793                    afterEnclosedExpr = true;
2794                }
2795            }
2796        } catch (StringIndexOutOfBoundsException JavaDoc err) {
2797            grumble("No closing end tag found for direct element constructor");
2798        }
2799    }
2800
2801    private Expression parsePIConstructor() throws StaticError {
2802        try {
2803            FastStringBuffer pi = new FastStringBuffer(120);
2804            int firstSpace = -1;
2805            while (!pi.toString().endsWith("?>")) {
2806                char c = t.nextChar();
2807                if (firstSpace < 0 && " \t\r\n".indexOf(c) >= 0) {
2808                    firstSpace = pi.length();
2809                }
2810                pi.append(c);
2811            }
2812            pi.setLength(pi.length() - 2);
2813
2814            String JavaDoc target;
2815            String JavaDoc data = "";
2816            if (firstSpace < 0) {
2817                // there is no data part
2818
target = pi.toString();
2819            } else {
2820                // trim leading space from the data part, but not trailing space
2821
target = pi.toString().substring(0, firstSpace);
2822                firstSpace++;
2823                while (firstSpace < pi.length() && " \t\r\n".indexOf(pi.charAt(firstSpace)) >= 0) {
2824                    firstSpace++;
2825                }
2826                data = pi.toString().substring(firstSpace);
2827            }
2828
2829            if (!XMLChar.isValidNCName(target)) {
2830                grumble("Invalid processing instruction name " + Err.wrap(target));
2831            }
2832
2833            ProcessingInstruction instruction =
2834                    new ProcessingInstruction(new StringValue(target));
2835            instruction.setSelect(StringValue.makeStringValue(data));
2836            setLocation(instruction);
2837            return instruction;
2838        } catch (StringIndexOutOfBoundsException JavaDoc err) {
2839            grumble("No closing '?>' found for processing instruction");
2840            return null;
2841        }
2842    }
2843
2844    private void readCDATASection(FastStringBuffer cdata) throws StaticError {
2845        try {
2846            char c;
2847            // CDATA section
2848
c = t.nextChar();
2849            expectChar(c, 'C');
2850            c = t.nextChar();
2851            expectChar(c, 'D');
2852            c = t.nextChar();
2853            expectChar(c, 'A');
2854            c = t.nextChar();
2855            expectChar(c, 'T');
2856            c = t.nextChar();
2857            expectChar(c, 'A');
2858            c = t.nextChar();
2859            expectChar(c, '[');
2860            while (!cdata.toString().endsWith("]]>")) {
2861                cdata.append(t.nextChar());
2862            }
2863            cdata.setLength(cdata.length() - 3);
2864        } catch (StringIndexOutOfBoundsException JavaDoc err) {
2865            grumble("No closing ']]>' found for CDATA section");
2866        }
2867    }
2868
2869    private Expression parseCommentConstructor() throws StaticError {
2870        try {
2871            char c = t.nextChar();
2872            ;
2873            // XML-like comment
2874
expectChar(c, '-');
2875            FastStringBuffer comment = new FastStringBuffer(240);
2876            while (!comment.toString().endsWith("--")) {
2877                comment.append(t.nextChar());
2878            }
2879            if (t.nextChar() != '>') {
2880                grumble("'--' is not permitted in an XML comment");
2881            }
2882            CharSequence JavaDoc commentText = comment.subSequence(0, comment.length() - 2);
2883            Comment instruction = new Comment();
2884            instruction.setSelect(new StringValue(commentText));
2885            setLocation(instruction);
2886            return instruction;
2887        } catch (StringIndexOutOfBoundsException JavaDoc err) {
2888            grumble("No closing '-->' found for comment constructor");
2889            return null;
2890        }
2891    }
2892
2893
2894    /**
2895     * Convert an expression so it generates a space-separated sequence of strings
2896     * @param exp the expression that calculates the content
2897     * @param noNodeIfEmpty if true, no node is produced when the value of the content
2898     * expression is an empty sequence. If false, the effect of supplying an empty sequence
2899     * is that a node is created whose string-value is a zero-length string. Set to true for
2900     * text node constructors, false for other kinds of node.
2901     */

2902
2903    private Expression stringify(Expression exp, boolean noNodeIfEmpty) throws XPathException {
2904        return new QuerySimpleContentConstructor(exp, StringValue.SINGLE_SPACE, noNodeIfEmpty).simplify(env);
2905    }
2906
2907    /**
2908     * Method to make a string literal from a token identified as a string
2909     * literal. This is trivial in XPath, but in XQuery the method is overridden
2910     * to identify pseudo-XML character and entity references
2911     * @param token
2912     * @return The string value of the string literal, after dereferencing entity and
2913     * character references
2914     */

2915
2916    protected StringValue makeStringLiteral(String JavaDoc token) throws StaticError {
2917        if (token.indexOf('&') == -1) {
2918            return StringValue.makeStringValue(token);
2919        } else {
2920            FastStringBuffer sb = new FastStringBuffer(80);
2921            for (int i = 0; i < token.length(); i++) {
2922                char c = token.charAt(i);
2923                if (c == '&') {
2924                    int semic = token.indexOf(';', i);
2925                    if (semic < 0) {
2926                        grumble("No closing ';' found for entity or character reference");
2927                    } else {
2928                        String JavaDoc entity = token.substring(i + 1, semic);
2929                        sb.append(analyzeEntityReference(entity));
2930                        i = semic;
2931                    }
2932                } else {
2933                    sb.append(c);
2934                }
2935            }
2936            return StringValue.makeStringValue(sb);
2937        }
2938    }
2939
2940    /**
2941     * Read a pseudo-XML character reference or entity reference.
2942     * @return The character represented by the character or entity reference. Note
2943     * that this is a string rather than a char because a char only accommodates characters
2944     * up to 65535.
2945     * @throws net.sf.saxon.trans.StaticError if the character or entity reference is not well-formed
2946     */

2947
2948    private String JavaDoc readEntityReference() throws StaticError {
2949        try {
2950            FastStringBuffer sb = new FastStringBuffer(40);
2951            while (true) {
2952                char c = t.nextChar();
2953                if (c == ';') break;
2954                sb.append(c);
2955            }
2956            String JavaDoc entity = sb.toString();
2957            return analyzeEntityReference(entity);
2958        } catch (StringIndexOutOfBoundsException JavaDoc err) {
2959            grumble("No closing ';' found for entity or character reference");
2960        }
2961        return null; // to keep the Java compiler happy
2962
}
2963
2964    private String JavaDoc analyzeEntityReference(String JavaDoc entity) throws StaticError {
2965        if ("lt".equals(entity)) {
2966            return "<";
2967        } else if ("gt".equals(entity)) {
2968            return ">";
2969        } else if ("amp".equals(entity)) {
2970            return "&";
2971        } else if ("quot".equals(entity)) {
2972            return "\"";
2973        } else if ("apos".equals(entity)) {
2974            return "'";
2975        } else if (entity.length() < 2 || entity.charAt(0) != '#') {
2976            grumble("invalid entity reference &" + entity + ';');
2977            return null;
2978        } else {
2979            entity = entity.toLowerCase();
2980            return parseCharacterReference(entity);
2981        }
2982    }
2983
2984    private String JavaDoc parseCharacterReference(String JavaDoc entity) throws StaticError {
2985        int value = 0;
2986        if (entity.charAt(1) == 'x') {
2987            for (int i = 2; i < entity.length(); i++) {
2988                int digit = "0123456789abcdef".indexOf(entity.charAt(i));
2989                if (digit < 0) {
2990                    grumble("invalid character '" + entity.charAt(i) + "' in hex character reference");
2991                }
2992                value = (value * 16) + digit;
2993            }
2994        } else {
2995            for (int i = 1; i < entity.length(); i++) {
2996                int digit = "0123456789".indexOf(entity.charAt(i));
2997                if (digit < 0) {
2998                    grumble("invalid character '" + entity.charAt(i) + "' in decimal character reference");
2999                }
3000                value = (value * 10) + digit;
3001            }
3002        }
3003        // following code borrowed from AElfred
3004
// check for character refs being legal XML
3005
if ((value < 0x0020
3006                && !(value == '\n' || value == '\t' || value == '\r'))
3007                || (value >= 0xD800 && value <= 0xDFFF)
3008                || value == 0xFFFE || value == 0xFFFF
3009                || value > 0x0010ffff)
3010            grumble("Invalid XML character reference x"
3011                    + Integer.toHexString(value));
3012
3013        // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
3014
// (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
3015
if (value <= 0x0000ffff) {
3016            // no surrogates needed
3017
return "" + (char) value;
3018        } else if (value <= 0x0010ffff) {
3019            value -= 0x10000;
3020            // > 16 bits, surrogate needed
3021
return "" + ((char) (0xd800 | (value >> 10)))
3022                    + ((char) (0xdc00 | (value & 0x0003ff)));
3023        } else {
3024            // too big for surrogate
3025
grumble("Character reference x" + Integer.toHexString(value) + " is too large");
3026        }
3027        return null;
3028    }
3029
3030    /**
3031     * Lookahead one token, catching any exception thrown by the tokenizer. This
3032     * method is only called from the query parser when switching from character-at-a-time
3033     * mode to tokenizing mode
3034     */

3035
3036    private void lookAhead() throws StaticError {
3037        try {
3038            t.lookAhead();
3039        } catch (XPathException err) {
3040            grumble(err.getMessage());
3041        }
3042    }
3043
3044    /**
3045     * Skip whitespace.
3046     * @param c the current character
3047     * @return the first character after any whitespace
3048     */

3049
3050    private char skipSpaces(char c) {
3051        while (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
3052            c = t.nextChar();
3053        }
3054        return c;
3055    }
3056
3057    /**
3058     * Test whether the current character is the expected character.
3059     * @param actual The character that was read
3060     * @param expected The character that was expected
3061     * @throws net.sf.saxon.trans.StaticError if they are different
3062     */

3063
3064    private void expectChar(char actual, char expected) throws StaticError {
3065        if (actual != expected) {
3066            grumble("Expected '" + expected + "', found '" + actual + '\'');
3067        }
3068    }
3069
3070    /**
3071     * Get the current language (XPath or XQuery)
3072     */

3073
3074    protected String JavaDoc getLanguage() {
3075        return "XQuery";
3076    }
3077
3078    private static class AttributeDetails {
3079        String JavaDoc value;
3080        int startOffset;
3081    }
3082}
3083
3084//
3085
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
3086
// you may not use this file except in compliance with the License. You may obtain a copy of the
3087
// License at http://www.mozilla.org/MPL/
3088
//
3089
// Software distributed under the License is distributed on an "AS IS" basis,
3090
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
3091
// See the License for the specific language governing rights and limitations under the License.
3092
//
3093
// The Original Code is: all this file.
3094
//
3095
// The Initial Developer of the Original Code is Michael H. Kay
3096
//
3097
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
3098
//
3099
// Contributor(s): none.
3100
//
3101
Popular Tags