001: package net.sf.saxon;
002:
003: import net.sf.saxon.event.Builder;
004: import net.sf.saxon.event.SaxonOutputKeys;
005: import net.sf.saxon.instruct.TerminationException;
006: import net.sf.saxon.om.DocumentInfo;
007: import net.sf.saxon.om.SequenceIterator;
008: import net.sf.saxon.om.Validation;
009: import net.sf.saxon.query.*;
010: import net.sf.saxon.trace.TraceListener;
011: import net.sf.saxon.trace.XQueryTraceListener;
012: import net.sf.saxon.trans.DynamicError;
013: import net.sf.saxon.trans.XPathException;
014: import net.sf.saxon.value.UntypedAtomicValue;
015: import net.sf.saxon.value.Whitespace;
016: import org.xml.sax.InputSource;
017:
018: import javax.xml.transform.Source;
019: import javax.xml.transform.TransformerFactoryConfigurationError;
020: import javax.xml.transform.sax.SAXSource;
021: import javax.xml.transform.stream.StreamResult;
022: import javax.xml.transform.stream.StreamSource;
023: import java.io.*;
024: import java.util.Date;
025: import java.util.Properties;
026:
027: /**
028: * This <B>Query</B> class provides a command-line interface to the Saxon XQuery processor.<p>
029: * <p/>
030: * The XQuery syntax supported conforms to the W3C XQuery 1.0 drafts.
031: *
032: * @author Michael H. Kay
033: */
034:
035: public class Query {
036:
037: Configuration config;
038:
039: /**
040: * Set the configuration. This is designed to be
041: * overridden in a subclass
042: */
043:
044: protected Configuration makeConfiguration() {
045: Configuration config = new Configuration();
046: config.setAllNodesUntyped(true);
047: return config;
048: }
049:
050: /**
051: * Get the configuration in use
052: *
053: * @return the configuration
054: */
055:
056: protected Configuration getConfiguration() {
057: return config;
058: }
059:
060: /**
061: * Main program, can be used directly from the command line.
062: * <p>The format is:</P>
063: * <p>java net.sf.saxon.Query [options] <I>query-file</I> ><I>output-file</I></P>
064: * <p>followed by any number of parameters in the form {keyword=value}... which can be
065: * referenced from within the query.</p>
066: * <p>This program executes the query in query-file.</p>
067: *
068: * @param args List of arguments supplied on operating system command line
069: * @throws Exception Indicates that a compile-time or
070: * run-time error occurred
071: */
072:
073: public static void main(String args[]) throws Exception {
074: // the real work is delegated to another routine so that it can be used in a subclass
075: (new Query()).doMain(args, "java net.sf.saxon.Query");
076: }
077:
078: /**
079: * Support method for main program. This support method can also be invoked from subclasses
080: * that support the same command line interface
081: *
082: * @param args the command-line arguments
083: * @param name name of the class, to be used in error messages
084: */
085:
086: protected void doMain(String args[], String name) {
087: config = makeConfiguration();
088: config.setHostLanguage(Configuration.XQUERY);
089: boolean schemaAware = config
090: .isSchemaAware(Configuration.XQUERY);
091: boolean showTime = false;
092: int repeat = 1;
093: StaticQueryContext staticEnv = new StaticQueryContext(config);
094: DynamicQueryContext dynamicEnv = new DynamicQueryContext(config);
095: String sourceFileName = null;
096: String queryFileName = null;
097: File sourceFile;
098: File outputFile;
099: boolean useURLs = false;
100: String outputFileName = null;
101: boolean explain = false;
102: boolean wrap = false;
103: boolean pullMode = false;
104:
105: Properties outputProps = new Properties();
106:
107: // Check the command-line arguments.
108:
109: try {
110: int i = 0;
111: while (i < args.length) {
112:
113: if (args[i].charAt(0) == '-') {
114:
115: if (args[i].equals("-cr")) {
116: i++;
117: if (args.length < i + 1) {
118: badUsage(name, "No output file name");
119: }
120: String crclass = args[i++];
121: Object resolver = config.getInstance(crclass,
122: null);
123: if (!(resolver instanceof CollectionURIResolver)) {
124: quit(
125: crclass
126: + " is not a CollectionURIResolver",
127: 2);
128: }
129: config
130: .setCollectionURIResolver((CollectionURIResolver) resolver);
131:
132: } else if (args[i].equals("-ds")) {
133: config.setTreeModel(Builder.STANDARD_TREE);
134: i++;
135: } else if (args[i].equals("-dt")) {
136: config.setTreeModel(Builder.TINY_TREE);
137: i++;
138: } else if (args[i].equals("-e")) {
139: explain = true;
140: i++;
141: } else if (args[i].equals("-l")) {
142: config.setLineNumbering(true);
143: i++;
144: } else if (args[i].equals("-3")) { // undocumented option: do it thrice
145: i++;
146: repeat = 3;
147: } else if (args[i].equals("-9")) { // undocumented option: do it nine times
148: i++;
149: repeat = 9;
150: } else if (args[i].equals("-mr")) {
151: i++;
152: if (args.length < i + 1) {
153: badUsage(name, "No ModuleURIResolver class");
154: }
155: String r = args[i++];
156: config.setModuleURIResolver(r);
157: } else if (args[i].equals("-noext")) {
158: i++;
159: config.setAllowExternalFunctions(false);
160: } else if (args[i].equals("-o")) {
161: i++;
162: if (args.length < i + 1) {
163: badUsage(name, "No output file name");
164: }
165: outputFileName = args[i++];
166: } else if (args[i].equals("-p")) {
167: i++;
168: setPOption(config);
169: useURLs = true;
170: } else if (args[i].equals("-pull")) {
171: i++;
172: pullMode = true;
173: } else if (args[i].equals("-r")) {
174: i++;
175: if (args.length < i + 1) {
176: badUsage(name, "No URIResolver class");
177: }
178: String r = args[i++];
179: config
180: .setURIResolver(config
181: .makeURIResolver(r));
182: dynamicEnv.setURIResolver(config
183: .makeURIResolver(r));
184: } else if (args[i].equals("-s")) {
185: i++;
186: if (args.length < i + 1) {
187: badUsage(name, "No source file name");
188: }
189: sourceFileName = args[i++];
190: } else if (args[i].equals("-snone")) {
191: config.setStripsWhiteSpace(Whitespace.NONE);
192: i++;
193: } else if (args[i].equals("-sall")) {
194: config.setStripsWhiteSpace(Whitespace.ALL);
195: i++;
196: } else if (args[i].equals("-signorable")) {
197: config
198: .setStripsWhiteSpace(Whitespace.IGNORABLE);
199: i++;
200: } else if (args[i].equals("-strip")) { // retained for compatibility
201: config.setStripsWhiteSpace(Whitespace.ALL);
202: i++;
203: } else if (args[i].equals("-t")) {
204: System.err.println(config.getProductTitle());
205: System.err.println("Java version "
206: + System.getProperty("java.version"));
207: config.setTiming(true);
208: showTime = true;
209: i++;
210: } else if (args[i].equals("-T")) {
211: config
212: .setTraceListener(new XQueryTraceListener());
213: i++;
214: } else if (args[i].equals("-TJ")) {
215: i++;
216: config.setTraceExternalFunctions(true);
217: } else if (args[i].equals("-TL")) {
218: if (args.length < i + 2) {
219: badUsage(name,
220: "No TraceListener class specified");
221: }
222: TraceListener traceListener = config
223: .makeTraceListener(args[++i]);
224: config.setTraceListener(traceListener);
225: config.setLineNumbering(true);
226: i++;
227: } else if (args[i].equals("-u")) {
228: useURLs = true;
229: i++;
230: } else if (args[i].equals("-v")) {
231: config.setValidation(true);
232: i++;
233: } else if (args[i].equals("-val")) {
234: if (schemaAware) {
235: config
236: .setSchemaValidationMode(Validation.STRICT);
237: } else {
238: quit(
239: "The -val option requires a schema-aware processor",
240: 2);
241: }
242: i++;
243: } else if (args[i].equals("-vlax")) {
244: if (schemaAware) {
245: config
246: .setSchemaValidationMode(Validation.LAX);
247: } else {
248: quit(
249: "The -vlax option requires a schema-aware processor",
250: 2);
251: }
252: i++;
253: } else if (args[i].equals("-vw")) {
254: if (schemaAware) {
255: config.setValidationWarnings(true);
256: } else {
257: quit(
258: "The -vw option requires a schema-aware processor",
259: 2);
260: }
261: i++;
262: } else if (args[i].equals("-wrap")) {
263: wrap = true;
264: i++;
265: } else if (args[i].equals("-1.1")) {
266: config.setXMLVersion(Configuration.XML11);
267: i++;
268: } else if (args[i].equals("-?")) {
269: badUsage(name, "");
270: } else if (args[i].equals("-")) {
271: queryFileName = "-";
272: i++;
273: } else {
274: badUsage(name, "Unknown option " + args[i]);
275: }
276: } else {
277: break;
278: }
279: }
280:
281: if (!("-".equals(queryFileName))) {
282: if (args.length < i + 1) {
283: badUsage(name, "No query file name");
284: }
285: queryFileName = args[i++];
286: }
287:
288: for (int p = i; p < args.length; p++) {
289: String arg = args[p];
290: int eq = arg.indexOf("=");
291: if (eq < 1 || eq >= arg.length() - 1) {
292: badUsage(name,
293: "Bad param=value pair on command line: "
294: + arg);
295: }
296: String argname = arg.substring(0, eq);
297: if (argname.startsWith("!")) {
298: // parameters starting with "!" are taken as output properties
299: outputProps.setProperty(argname.substring(1), arg
300: .substring(eq + 1));
301: } else if (argname.startsWith("+")) {
302: // parameters starting with "+" are taken as input documents
303: Object sources = Transform.loadDocuments(arg
304: .substring(eq + 1), useURLs, config, true);
305: dynamicEnv.setParameter(argname.substring(1),
306: sources);
307: } else {
308: dynamicEnv.setParameter(argname,
309: new UntypedAtomicValue(arg
310: .substring(eq + 1)));
311: }
312: }
313:
314: config.displayLicenseMessage();
315: if (pullMode) {
316: config.setLazyConstructionMode(true);
317: }
318:
319: Source sourceInput = null;
320:
321: if (sourceFileName != null) {
322: if (useURLs || sourceFileName.startsWith("http:")
323: || sourceFileName.startsWith("file:")) {
324: sourceInput = config.getURIResolver().resolve(
325: sourceFileName, null);
326: if (sourceInput == null) {
327: sourceInput = config.getSystemURIResolver()
328: .resolve(sourceFileName, null);
329: }
330: } else if (sourceFileName.equals("-")) {
331: // take input from stdin
332: sourceInput = new SAXSource(new InputSource(
333: System.in));
334: } else {
335: sourceFile = new File(sourceFileName);
336: if (!sourceFile.exists()) {
337: quit("Source file " + sourceFile
338: + " does not exist", 2);
339: }
340:
341: InputSource eis = new InputSource(sourceFile
342: .toURI().toString());
343: sourceInput = new SAXSource(eis);
344: }
345: }
346:
347: long startTime = (new Date()).getTime();
348: if (showTime) {
349: System.err.println("Compiling query from "
350: + queryFileName);
351: }
352:
353: XQueryExpression exp;
354:
355: try {
356: if (queryFileName.equals("-")) {
357: Reader queryReader = new InputStreamReader(
358: System.in);
359: exp = staticEnv.compileQuery(queryReader);
360: } else if (queryFileName.startsWith("{")
361: && queryFileName.endsWith("}")) {
362: // query is inline on the command line
363: String q = queryFileName.substring(1, queryFileName
364: .length() - 1);
365: exp = staticEnv.compileQuery(q);
366: } else if (useURLs || queryFileName.startsWith("http:")
367: || queryFileName.startsWith("file:")) {
368: ModuleURIResolver resolver = config
369: .getModuleURIResolver();
370: String[] locations = { queryFileName };
371: StreamSource[] sources = resolver.resolve(null,
372: null, locations);
373: if (sources.length != 1) {
374: quit(
375: "Module URI Resolver must return a single StreamSource",
376: 2);
377: }
378: String queryText = QueryReader.readSourceQuery(
379: sources[0], config.getNameChecker());
380: exp = staticEnv.compileQuery(queryText);
381: } else {
382: InputStream queryStream = new FileInputStream(
383: queryFileName);
384: staticEnv.setBaseURI(new File(queryFileName)
385: .toURI().toString());
386: exp = staticEnv.compileQuery(queryStream, null);
387: }
388: staticEnv = exp.getStaticContext(); // the original staticContext is copied
389:
390: if (showTime) {
391: long endTime = (new Date()).getTime();
392: System.err.println("Compilation time: "
393: + (endTime - startTime) + " milliseconds");
394: startTime = endTime;
395: }
396:
397: } catch (XPathException err) {
398: int line = -1;
399: String module = null;
400: if (err.getLocator() != null) {
401: line = err.getLocator().getLineNumber();
402: module = err.getLocator().getSystemId();
403: }
404: if (err.hasBeenReported()) {
405: quit("Failed to compile query", 2);
406: } else {
407: if (line == -1) {
408: System.err.println("Failed to compile query: "
409: + err.getMessage());
410: } else {
411: System.err.println("Static error at line "
412: + line + " of " + module + ':');
413: System.err.println(err.getMessage());
414: }
415: }
416: exp = null;
417: System.exit(2);
418: }
419:
420: if (explain) {
421: staticEnv.explainGlobalVariables();
422: staticEnv.explainGlobalFunctions();
423: exp.explain(staticEnv.getNamePool());
424: }
425:
426: OutputStream destination;
427: if (outputFileName != null) {
428: outputFile = new File(outputFileName);
429: if (outputFile.isDirectory()) {
430: quit("Output is a directory", 2);
431: }
432: destination = new FileOutputStream(outputFile);
433: } else {
434: destination = System.out;
435: }
436:
437: for (int r = 0; r < repeat; r++) { // repeat is for internal testing/timing
438:
439: if (sourceInput != null) {
440: if (showTime) {
441: System.err.println("Processing "
442: + sourceInput.getSystemId());
443: }
444: DocumentInfo doc = staticEnv
445: .buildDocument(sourceInput);
446: dynamicEnv.setContextNode(doc);
447: }
448:
449: try {
450: if (wrap) {
451: SequenceIterator results = exp
452: .iterator(dynamicEnv);
453: DocumentInfo resultDoc = QueryResult.wrap(
454: results, config);
455: QueryResult.serialize(resultDoc,
456: new StreamResult(destination),
457: outputProps, config);
458: destination.close();
459: } else if (pullMode) {
460: if (wrap) {
461: outputProps.setProperty(
462: SaxonOutputKeys.WRAP, "yes");
463: }
464: try {
465: exp.pull(dynamicEnv, new StreamResult(
466: destination), outputProps);
467: } catch (XPathException err) {
468: config.reportFatalError(err);
469: throw err;
470: }
471: } else {
472: exp.run(dynamicEnv, new StreamResult(
473: destination), outputProps);
474: }
475: } catch (TerminationException err) {
476: throw err;
477: } catch (XPathException err) {
478: if (err.hasBeenReported()) {
479: throw new DynamicError(
480: "Run-time errors were reported");
481: } else {
482: throw err;
483: }
484: }
485:
486: if (showTime) {
487: long endTime = (new Date()).getTime();
488: System.err.println("Execution time: "
489: + (endTime - startTime) + " milliseconds");
490: startTime = endTime;
491: }
492: }
493:
494: } catch (TerminationException err) {
495: quit(err.getMessage(), 1);
496: } catch (XPathException err) {
497: quit("Query processing failed: " + err.getMessage(), 2);
498: } catch (TransformerFactoryConfigurationError err) {
499: err.printStackTrace();
500: quit("Query processing failed", 2);
501: } catch (Exception err2) {
502: err2.printStackTrace();
503: quit("Fatal error during transformation: "
504: + err2.getMessage(), 2);
505: }
506: }
507:
508: /**
509: * Exit with a message
510: *
511: * @param message The message to be output
512: * @param code The result code to be returned to the operating
513: * system shell
514: */
515:
516: protected static void quit(String message, int code) {
517: System.err.println(message);
518: System.exit(code);
519: }
520:
521: public void setPOption(Configuration config) {
522: config.getSystemURIResolver().setRecognizeQueryParameters(true);
523: }
524:
525: /**
526: * Report incorrect usage of the command line, with a list of the options and arguments that are available
527: *
528: * @param name The name of the command being executed (allows subclassing)
529: * @param message The error message
530: */
531: protected void badUsage(String name, String message) {
532: if (!"".equals(message)) {
533: System.err.println(message);
534: }
535: System.err.println(config.getProductTitle());
536: System.err.println("Usage: " + name
537: + " [options] query {param=value}...");
538: System.err.println("Options: ");
539: System.err
540: .println(" -cr classname Use specified CollectionURIResolver class");
541: System.err
542: .println(" -ds Use standard tree data structure");
543: System.err
544: .println(" -dt Use tinytree data structure (default)");
545: System.err
546: .println(" -e Explain optimized query expression");
547: System.err
548: .println(" -mr classname Use specified ModuleURIResolver class");
549: System.err
550: .println(" -noext Disallow calls to Java methods");
551: System.err
552: .println(" -o filename Send output to named file");
553: System.err
554: .println(" -p Recognize Saxon file extensions and query parameters");
555: System.err.println(" -pull Run query in pull mode");
556: System.err
557: .println(" -r classname Use specified URIResolver class");
558: System.err
559: .println(" -s file|URI Provide initial context document");
560: System.err
561: .println(" -sall Strip all whitespace text nodes");
562: System.err
563: .println(" -signorable Strip ignorable whitespace text nodes (default)");
564: System.err
565: .println(" -snone Strip no whitespace text nodes");
566: System.err
567: .println(" -t Display version and timing information");
568: System.err.println(" -T Trace query execution");
569: System.err
570: .println(" -TJ Trace calls to external Java functions");
571: System.err
572: .println(" -TL classname Trace query execution to user-defined trace listener");
573: System.err
574: .println(" -u Names are URLs not filenames");
575: System.err
576: .println(" -v Validate source documents using DTD");
577: if (config.isSchemaAware(Configuration.XQUERY)) {
578: System.err
579: .println(" -val Validate source documents using schema");
580: System.err
581: .println(" -vlax Lax validation of source documents using schema");
582: System.err
583: .println(" -vw Treat validation errors on result document as warnings");
584: }
585: System.err
586: .println(" -wrap Wrap result sequence in XML elements");
587: System.err.println(" -1.1 Allow XML 1.1 documents");
588: System.err.println(" -? Display this message ");
589: System.err
590: .println(" param=value Set query string parameter");
591: System.err
592: .println(" +param=value Set query document parameter");
593: System.err
594: .println(" !option=value Set serialization option");
595: if ("".equals(message)) {
596: System.exit(0);
597: } else {
598: System.exit(2);
599: }
600: }
601:
602: }
603:
604: //
605: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
606: // you may not use this file except in compliance with the License. You may obtain a copy of the
607: // License at http://www.mozilla.org/MPL/
608: //
609: // Software distributed under the License is distributed on an "AS IS" basis,
610: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
611: // See the License for the specific language governing rights and limitations under the License.
612: //
613: // The Original Code is: all this file.
614: //
615: // The Initial Developer of the Original Code is Michael H. Kay.
616: //
617: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
618: //
619: // Contributor(s): changes to allow source and/or stylesheet from stdin contributed by
620: // Gunther Schadow [gunther@aurora.regenstrief.org]
621: //
|