PageRenderTime 7ms CodeModel.GetById 6ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/saxonB/net/sf/saxon/Query.java

https://bitbucket.org/dmwelch/phdxnat_pipeline
Java | 1037 lines | 824 code | 69 blank | 144 comment | 345 complexity | 84e0c8da679a58f604ebf8bfacd422ca MD5 | raw file
   1package net.sf.saxon;
   2
   3import net.sf.saxon.event.Builder;
   4import net.sf.saxon.event.ProxyReceiver;
   5import net.sf.saxon.event.Receiver;
   6import net.sf.saxon.event.SaxonOutputKeys;
   7import net.sf.saxon.expr.PathMap;
   8import net.sf.saxon.instruct.TerminationException;
   9import net.sf.saxon.om.*;
  10import net.sf.saxon.query.*;
  11import net.sf.saxon.trace.ExpressionPresenter;
  12import net.sf.saxon.trace.XQueryTraceListener;
  13import net.sf.saxon.trans.XPathException;
  14import net.sf.saxon.type.SchemaException;
  15import net.sf.saxon.value.UntypedAtomicValue;
  16import net.sf.saxon.value.Whitespace;
  17import org.xml.sax.InputSource;
  18
  19import javax.xml.transform.OutputKeys;
  20import javax.xml.transform.Source;
  21import javax.xml.transform.TransformerException;
  22import javax.xml.transform.TransformerFactoryConfigurationError;
  23import javax.xml.transform.sax.SAXSource;
  24import javax.xml.transform.stream.StreamResult;
  25import javax.xml.transform.stream.StreamSource;
  26import java.io.*;
  27import java.util.*;
  28
  29/**
  30 * This <B>Query</B> class provides a command-line interface to the Saxon XQuery processor.<p>
  31 * <p/>
  32 * The XQuery syntax supported conforms to the W3C XQuery 1.0 drafts.
  33 *
  34 * @author Michael H. Kay
  35 */
  36
  37public class Query {
  38
  39    protected Configuration config;
  40    protected boolean showTime = false;
  41    protected int repeat = 1;
  42    protected String sourceFileName = null;
  43    protected String queryFileName = null;
  44    protected boolean useURLs = false;
  45    protected String outputFileName = null;
  46    protected String moduleURIResolverClass = null;
  47    protected String uriResolverClass = null;
  48    protected boolean explain = false;
  49    protected boolean wrap = false;
  50    protected boolean pullMode = false;
  51    protected boolean projection = false;
  52    protected boolean updating = false;
  53    protected boolean writeback = false;
  54    protected boolean backup = true;
  55    protected String explainOutputFileName = null;
  56    //private PrintStream traceDestination = System.err;
  57    private boolean closeTraceDestination = false;
  58
  59    private boolean schemaAware = false;
  60
  61    /**
  62     * Set the configuration. This is designed to be
  63     * overridden in a subclass
  64     *
  65     * @param schemaAware true if a schema-aware configuration is required (in this case Saxon-SA must
  66     *                    be installed and licensed)
  67     * @param className   the name of the class to be loaded, representing the Configuration. This
  68     *                    allows additional control of the loading process under .NET
  69     * @return the successfully loaded Configuration
  70     */
  71
  72    protected Configuration makeConfiguration(boolean schemaAware, String className) {
  73        if (schemaAware) {
  74            config = Configuration.makeSchemaAwareConfiguration(null, className);
  75        } else {
  76            config = new Configuration();
  77            // In basic XQuery, all nodes are untyped when calling from the command line
  78            config.setAllNodesUntyped(true);
  79        }
  80        return config;
  81    }
  82
  83    /**
  84     * Get the configuration in use
  85     *
  86     * @return the configuration
  87     */
  88
  89    protected Configuration getConfiguration() {
  90        return config;
  91    }
  92
  93    /**
  94     * Main program, can be used directly from the command line.
  95     * <p>The format is:</P>
  96     * <p>java net.sf.saxon.Query [options] <I>query-file</I> &gt;<I>output-file</I></P>
  97     * <p>followed by any number of parameters in the form {keyword=value}... which can be
  98     * referenced from within the query.</p>
  99     * <p>This program executes the query in query-file.</p>
 100     *
 101     * @param args List of arguments supplied on operating system command line
 102     * @throws Exception Indicates that a compile-time or
 103     *                   run-time error occurred
 104     */
 105
 106    public static void main(String args[])
 107            throws Exception {
 108        // the real work is delegated to another routine so that it can be used in a subclass
 109        (new Query()).doQuery(args, "java net.sf.saxon.Query");
 110    }
 111
 112    /**
 113     * Support method for main program. This support method can also be invoked from subclasses
 114     * that support the same command line interface
 115     *
 116     * @param args    the command-line arguments
 117     * @param command name of the class, to be used in error messages
 118     */
 119
 120    protected void doQuery(String args[], String command) {
 121
 122        schemaAware = testIfSchemaAware(args);
 123
 124        config = makeConfiguration(schemaAware, null);
 125        config.setHostLanguage(Configuration.XQUERY);
 126
 127        StaticQueryContext staticEnv = new StaticQueryContext(config);
 128        DynamicQueryContext dynamicEnv = new DynamicQueryContext(config);
 129        Properties outputProps = new Properties();
 130
 131        // Check the command-line arguments.
 132
 133        try {
 134            parseOptions(args, command, dynamicEnv, outputProps);
 135
 136            if (updating) {
 137                staticEnv.setUpdatingEnabled(true);
 138            }
 139
 140            if (moduleURIResolverClass != null) {
 141                Object mr = config.getInstance(moduleURIResolverClass, null);
 142                if (!(mr instanceof ModuleURIResolver)) {
 143                    badUsage(command, moduleURIResolverClass + " is not a ModuleURIResolver");
 144                }
 145                staticEnv.setModuleURIResolver((ModuleURIResolver)mr);
 146            }
 147
 148            if (uriResolverClass != null) {
 149                config.setURIResolver(config.makeURIResolver(uriResolverClass));
 150                dynamicEnv.setURIResolver(config.makeURIResolver(uriResolverClass));
 151            }
 152
 153            config.displayLicenseMessage();
 154            if (pullMode) {
 155                //config.setLazyConstructionMode(true);
 156            }
 157
 158            if (explain) {
 159                config.setOptimizerTracing(true);
 160            }
 161
 162            Source sourceInput = null;
 163
 164            if (sourceFileName != null) {
 165                sourceInput = processSourceFile(sourceFileName, useURLs);
 166            }
 167
 168            long startTime = (new Date()).getTime();
 169            if (showTime) {
 170                System.err.println("Analyzing query from " + queryFileName);
 171            }
 172
 173            // Compile the query
 174
 175            XQueryExpression exp;
 176            try {
 177                exp = compileQuery(staticEnv, queryFileName, useURLs);
 178
 179                if (showTime) {
 180                    long endTime = (new Date()).getTime();
 181                    System.err.println("Analysis time: " + (endTime - startTime) + " milliseconds");
 182                    startTime = endTime;
 183                }
 184
 185            } catch (XPathException err) {
 186                int line = -1;
 187                String module = null;
 188                if (err.getLocator() != null) {
 189                    line = err.getLocator().getLineNumber();
 190                    module = err.getLocator().getSystemId();
 191                }
 192                if (err.hasBeenReported()) {
 193                    quit("Static error(s) in query", 2);
 194                } else {
 195                    if (line == -1) {
 196                        System.err.println("Static error in query: " + err.getMessage());
 197                    } else {
 198                        System.err.println("Static error at line " + line + " of " + module + ':');
 199                        System.err.println(err.getMessage());
 200                    }
 201                }
 202                exp = null;
 203                System.exit(2);
 204            }
 205
 206            if (explain) {
 207                explain(exp);
 208            }
 209
 210            // Load the source file (applying document projection if requested)
 211
 212            exp.setAllowDocumentProjection(projection);
 213            processSource(sourceInput, exp, dynamicEnv);
 214
 215            // Run the query (repeatedly, if the -repeat option was set)
 216
 217            long totalTime = 0;
 218            int r;
 219            for (r = 0; r < repeat; r++) {      // repeat is for internal testing/timing
 220                try {
 221                    OutputStream destination;
 222                    if (outputFileName != null) {
 223                        File outputFile = new File(outputFileName);
 224                        if (outputFile.isDirectory()) {
 225                            quit("Output is a directory", 2);
 226                        }
 227                        destination = new FileOutputStream(outputFile);
 228                    } else {
 229                        destination = System.out;
 230                    }
 231
 232                    runQuery(exp, dynamicEnv, destination, outputProps);
 233                } catch (TerminationException err) {
 234                    throw err;
 235                } catch (XPathException err) {
 236                    if (err.hasBeenReported()) {
 237                        //err.printStackTrace();
 238                        throw new XPathException("Run-time errors were reported");
 239                    } else {
 240                        throw err;
 241                    }
 242                }
 243
 244                if (showTime) {
 245                    long endTime = (new Date()).getTime();
 246                    if (r > 3) {
 247                        totalTime += (endTime - startTime);
 248                    }
 249                    if (repeat != 100) {
 250                        System.err.println("Execution time: " + (endTime - startTime) + " milliseconds");
 251                    } else if (totalTime > 100000) {
 252                        break;
 253                    }
 254                    startTime = endTime;
 255                }
 256            }
 257
 258            if (repeat > 3) {
 259                System.err.println("Average execution time: " + (totalTime / (double)(r - 3)) + " milliseconds");
 260            }
 261
 262        } catch (TerminationException err) {
 263            quit(err.getMessage(), 1);
 264        } catch (XPathException err) {
 265            quit("Query processing failed: " + err.getMessage(), 2);
 266        } catch (TransformerFactoryConfigurationError err) {
 267            err.printStackTrace();
 268            quit("Query processing failed", 2);
 269        } catch (SchemaException err) {
 270            quit("Schema processing failed: " + err.getMessage(), 2);
 271        } catch (Exception err2) {
 272            err2.printStackTrace();
 273            quit("Fatal error during query: " + err2.getClass().getName() + ": " +
 274                    (err2.getMessage() == null ? " (no message)" : err2.getMessage()), 2);
 275        }
 276    }
 277
 278    /**
 279     * Prescan the command line arguments to see if any of them imply use of a schema-aware processor
 280     * @param args the command line arguments
 281     * @return true if a schema-aware processor is needed
 282     */
 283
 284    protected boolean testIfSchemaAware(String[] args) {
 285        for (int i = 0; i < args.length; i++) {
 286            if (args[i].equals("-sa") ||
 287                    args[i].startsWith("-val:") ||
 288                    args[i].equals("-val") ||
 289                    args[i].equals("-vlax") ||
 290                    args[i].equals("-p") ||
 291                    args[i].equals("-xsd:") ||
 292                    args[i].startsWith("-xsdversion:") ||
 293                    args[i].startsWith("-projection:") ||
 294                    args[i].startsWith("-update:")) {
 295                return true;
 296            }
 297        }
 298        return false;
 299    }
 300
 301    /**
 302     * Parse the options supplied on the command line
 303     * @param args the command line arguments
 304     * @param command the name of the command that was used (for diagnostics only)
 305     * @param dynamicEnv the XQuery dynamic context
 306     * @param outputProps the serialization properties
 307     * @throws TransformerException if failures occur. Note, the method may also invoke System.exit().
 308     */
 309
 310    protected void parseOptions(String[] args, String command, DynamicQueryContext dynamicEnv, Properties outputProps)
 311            throws TransformerException {
 312        int i = 0;
 313        String additionalSchemas = null;
 314        while (i < args.length) {
 315
 316            if (args[i].charAt(0) == '-') {
 317                String option;
 318                String value = null;
 319                int colon = args[i].indexOf(':');
 320                if (colon > 0 && colon < args[i].length() - 1) {
 321                    option = args[i].substring(1, colon);
 322                    value = args[i].substring(colon + 1);
 323                } else {
 324                    option = args[i].substring(1);
 325                }
 326                if (option.equals("backup")) {
 327                    if (!("on".equals(value) || "off".equals(value))) {
 328                        badUsage(command, "-backup option must be -backup:on or -backup:off");
 329                    }
 330                    backup = "on".equals(value);
 331                    i++;
 332                } else if (option.equals("cr")) {                // collection resolver
 333                    i++;
 334                    if (value == null) {
 335                        if (args.length < i + 2) {
 336                            badUsage(command, "No resolver after -cr");
 337                        }
 338                        value = args[i++];
 339                    }
 340                    Object resolver = config.getInstance(value, null);
 341                    if (!(resolver instanceof CollectionURIResolver)) {
 342                        quit(value + " is not a CollectionURIResolver", 2);
 343                    }
 344                    config.setCollectionURIResolver((CollectionURIResolver)resolver);
 345
 346                } else if (option.equals("ds")) {         // linked tree
 347                    config.setTreeModel(Builder.LINKED_TREE);
 348                    i++;
 349                } else if (option.equals("dt")) {         // tiny tree (default)
 350                    config.setTreeModel(Builder.TINY_TREE);
 351                    i++;
 352                } else if (option.equals("dtd")) {
 353                    if (!("on".equals(value) || "off".equals(value))) {
 354                        badUsage(command, "-dtd option must be -dtd:on or -dtd:off");
 355                    }
 356                    config.setValidation("on".equals(value));
 357                    i++;
 358                } else if (option.equals("e")) {          // explain
 359                    explain = true;
 360                    i++;
 361                } else if (option.equals("expand")) {
 362                    if (!("on".equals(value) || "off".equals(value))) {
 363                        badUsage(command, "-expand option must be 'on' or 'off'");
 364                    }
 365                    config.setExpandAttributeDefaults("on".equals(value));
 366                    i++;
 367                } else if (option.equals("explain")) {          // explain
 368                    explain = true;
 369                    i++;
 370                } else if (option.startsWith("explain:")) {     // explain:filename
 371                    explain = true;
 372                    explainOutputFileName = value;
 373                    i++;
 374                } else if (option.equals("ext")) {
 375                    if (!("on".equals(value) || "off".equals(value))) {
 376                        badUsage(command, "-ext option must be -ext:on or -ext:off");
 377                    }
 378                    config.setAllowExternalFunctions("on".equals(value));
 379                    i++;
 380                } else if (option.equals("l")) {
 381                    if (!(value == null || "on".equals(value) || "off".equals(value))) {
 382                        badUsage(command, "-l option must be -l:on or -l:off");
 383                    }
 384                    config.setLineNumbering(!"off".equals(value));
 385                    i++;
 386
 387                    //i++;
 388                } else if (option.equals("mr")) {
 389                    i++;
 390                    if (value == null) {
 391                        if (args.length < i + 2) {
 392                            badUsage(command, "No resolver after -cr");
 393                        }
 394                        value = args[i++];
 395                    }
 396                    moduleURIResolverClass = value;
 397                } else if (option.equals("-noext")) {
 398                    i++;
 399                    config.setAllowExternalFunctions(false);
 400                } else if (option.equals("o")) {
 401                    i++;
 402                    if (value == null) {
 403                        if (args.length < i + 2) {
 404                            badUsage(command, "No output file name after -o");
 405                        }
 406                        value = args[i++];
 407                    }
 408                    outputFileName = value;
 409                } else if (option.equals("outval")) {
 410                    if (schemaAware) {
 411                        if (!(value == null || "recover".equals(value) || "fatal".equals(value))) {
 412                            badUsage(command, "-outval option must be 'recover' or 'fatal'");
 413                        }
 414                        config.setValidationWarnings("recover".equals(value));
 415                    } else {
 416                        quit("The -outval option requires a schema-aware processor", 2);
 417                    }
 418                    i++;
 419                } else if (option.equals("p")) {
 420                    i++;
 421                    if (!(value == null || "on".equals(value) || "off".equals(value))) {
 422                        badUsage(command, "-p option must be -p:on or -p:off");
 423                    }
 424                    if (!"off".equals(value)) {
 425                        config.setParameterizedURIResolver();
 426                        useURLs = true;
 427                    }
 428                } else if (option.equals("pipe")) {
 429                    i++;
 430                    if (!("push".equals(value) || "pull".equals(value))) {
 431                        badUsage(command, "-pipe option must be -p:push or -p:pull");
 432                    }
 433                    if ("pull".equals(value)) {
 434                        pullMode = true;
 435                    }
 436                } else if (option.equals("projection")) {
 437                    i++;
 438                    if (!(value == null || "on".equals(value) || "off".equals(value))) {
 439                        badUsage(command, "-projection option must be -projection:on or projection:off");
 440                    }
 441                    if (!"off".equals(value)) {
 442                        projection = true;
 443                    }
 444                } else if (option.equals("pull")) {
 445                    i++;
 446                    pullMode = true;
 447                } else if (option.equals("q")) {
 448                    i++;
 449                    queryFileName = value;
 450                } else if (option.equals("qs")) {
 451                    i++;
 452                    queryFileName = "{" + value + "}";
 453                } else if (option.equals("r")) {
 454                    i++;
 455                    if (value == null) {
 456                        if (args.length < i + 2) {
 457                            badUsage(command, "No URIResolver class after -r");
 458                        }
 459                        value = args[i++];
 460                    }
 461                    uriResolverClass = value;
 462                } else if (option.equals("repeat")) {
 463                    i++;
 464                    if (value == null) {
 465                        badUsage(command, "No number after -repeat");
 466                    } else {
 467                        try {
 468                            repeat = Integer.parseInt(value);
 469                        } catch (NumberFormatException err) {
 470                            badUsage(command, "Bad number after -repeat");
 471                        }
 472                    }
 473                } else if (option.equals("s")) {
 474                    i++;
 475                    if (value == null) {
 476                        if (args.length < i + 2) {
 477                            badUsage(command, "No source file name after -s");
 478                        }
 479                        value = args[i++];
 480                    }
 481                    sourceFileName = value;
 482                } else if (option.equals("sa")) {
 483                    // already handled
 484                    i++;
 485                } else if (option.equals("snone")) {
 486                    config.setStripsWhiteSpace(Whitespace.NONE);
 487                    i++;
 488                } else if (option.equals("sall")) {
 489                    config.setStripsWhiteSpace(Whitespace.ALL);
 490                    i++;
 491                } else if (option.equals("signorable")) {
 492                    config.setStripsWhiteSpace(Whitespace.IGNORABLE);
 493                    i++;
 494                } else if (option.equals("strip")) {
 495                    i++;
 496                    if (value == null) {
 497                        value = "all";
 498                    }
 499                    if ("none".equals(value)) {
 500                        // no action||  || "ignorable".equals(value)) {
 501                    } else if ("all".equals(value)) {
 502                        config.setStripsWhiteSpace(Whitespace.ALL);
 503                    } else if ("ignorable".equals(value)) {
 504                        config.setStripsWhiteSpace(Whitespace.IGNORABLE);
 505                    } else {
 506                        badUsage(command, "-strip must be none, all, or ignorable");
 507                    }
 508                } else if (option.equals("t")) {
 509                    System.err.println(config.getProductTitle());
 510                    System.err.println(Configuration.getPlatform().getPlatformVersion());
 511                    config.setTiming(true);
 512                    showTime = true;
 513                    i++;
 514                } else if (option.equals("traceout")) {
 515                    i++;
 516                    if (value.equals("#err")) {
 517                        // no action, this is the default
 518                    } else if (value.equals("#out")) {
 519                        dynamicEnv.setTraceFunctionDestination(System.out);
 520                    } else if (value.equals("#null")) {
 521                        dynamicEnv.setTraceFunctionDestination(null);
 522                    } else {
 523                        try {
 524                            dynamicEnv.setTraceFunctionDestination(
 525                                    new PrintStream(new FileOutputStream(new File(value))));
 526                            closeTraceDestination = true;
 527                        } catch (FileNotFoundException e) {
 528                            badUsage(command, "Trace output file " + value + " cannot be created");
 529                        }
 530                    }
 531                } else if (option.equals("tree")) {
 532                    if ("linked".equals(value)) {
 533                        config.setTreeModel(Builder.LINKED_TREE);
 534                    } else if ("tiny".equals(value)) {
 535                        config.setTreeModel(Builder.TINY_TREE);
 536                    } else {
 537                        badUsage(command, "-tree option must be 'linked' or 'tiny'");
 538                    }
 539                    i++;
 540                } else if (option.equals("T")) {
 541                    i++;
 542                    if (value == null) {
 543                        config.setTraceListener(new XQueryTraceListener());
 544                    } else {
 545                        config.setTraceListenerClass(value);
 546                    }
 547                    config.setLineNumbering(true);
 548                } else if (option.equals("TJ")) {
 549                    i++;
 550                    config.setTraceExternalFunctions(true);
 551                } else if (option.equals("TL")) {
 552                    if (args.length < i + 2) {
 553                        badUsage(command, "No TraceListener class specified");
 554                    }
 555                    config.setTraceListenerClass(args[++i]);
 556                    config.setLineNumbering(true);
 557                    i++;
 558                } else if (option.equals("u")) {
 559                    useURLs = true;
 560                    i++;
 561                } else if (option.equals("update")) {
 562                    i++;
 563                    if (!(value == null || "on".equals(value) || "off".equals(value) || "discard".equals(value))) {
 564                        badUsage(command, "-update option must be on|off|discard");
 565                    }
 566                    if (!"off".equals(value)) {
 567                        updating = true;
 568                    }
 569                    writeback = !("discard".equals(value));
 570                } else if (option.equals("untyped")) {
 571                    // TODO: this is an experimental undocumented option. It should be checked for consistency
 572                    config.setAllNodesUntyped(true);
 573                    i++;
 574                } else if (option.equals("v")) {
 575                    config.setValidation(true);
 576                    i++;
 577                } else if (option.equals("val")) {
 578                    if (!schemaAware) {
 579                        quit("The -val option requires a schema-aware processor", 2);
 580                    } else if (value == null || "strict".equals(value)) {
 581                        config.setSchemaValidationMode(Validation.STRICT);
 582                    } else if ("lax".equals(value)) {
 583                        config.setSchemaValidationMode(Validation.LAX);
 584                    } else {
 585                        badUsage(command, "-val option must be 'strict' or 'lax'");
 586                    }
 587                    i++;
 588                } else if (option.equals("vlax")) {
 589                    if (schemaAware) {
 590                        config.setSchemaValidationMode(Validation.LAX);
 591                    } else {
 592                        quit("The -vlax option requires a schema-aware processor", 2);
 593                    }
 594                    i++;
 595                } else if (option.equals("vw")) {
 596                    if (schemaAware) {
 597                        config.setValidationWarnings(true);
 598                    } else {
 599                        quit("The -vw option requires a schema-aware processor", 2);
 600                    }
 601                    i++;
 602                } else if (option.equals("wrap")) {
 603                    if (!(value == null || "on".equals(value) || "off".equals(value))) {
 604                        badUsage(command, "-wrap option must be -wrap:on or -wrap:off");
 605                    }
 606                    if (!"off".equals(value)) {
 607                        wrap = true;
 608                    }
 609                    i++;
 610                } else if (option.equals("x")) {
 611                    i++;
 612                    config.setSourceParserClass(value);
 613                } else if (option.equals("xi")) {
 614                    if (!(value == null || "on".equals(value) || "off".equals(value))) {
 615                        badUsage(command, "-xi option must be -xi:on or -xi:off");
 616                    }
 617                    if (!"off".equals(value)) {
 618                        config.setXIncludeAware(true);
 619                    }
 620                    i++;
 621                } else if (option.equals("1.1")) {
 622                    config.setXMLVersion(Configuration.XML11);
 623                    i++;
 624                } else if (option.equals("xmlversion")) {
 625                    i++;
 626                    if ("1.0".equals(value)) {
 627                        config.setXMLVersion(Configuration.XML10);
 628                    } else if ("1.1".equals(value)) {
 629                        config.setXMLVersion(Configuration.XML11);
 630                    } else {
 631                        badUsage(command, "-xmlversion must be 1.0 or 1.1");
 632                    }
 633                } else if (option.equals("xsd")) {
 634                    i++;
 635                    additionalSchemas = value;
 636                } else if (option.equals("xsdversion")) {    // XSD 1.1
 637                    i++;
 638                    if (!("1.0".equals(value) | "1.1".equals(value))) {
 639                        badUsage(command, "-xsdversion must be 1.0 or 1.1");
 640                    }
 641                    config.setConfigurationProperty(FeatureKeys.XSD_VERSION, value);
 642                } else if (option.equals("xsiloc")) {
 643                    i++;
 644                    if ("off".equals(value)) {
 645                        config.setConfigurationProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION, Boolean.FALSE);
 646                    } else if ("on".equals(value)) {
 647                        config.setConfigurationProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION, Boolean.TRUE);
 648                    } else {
 649                        badUsage(value, "format: -xsiloc:(on|off)");
 650                    }
 651                } else if (args[i].equals("-?")) {
 652                    badUsage(command, "");
 653                } else if (args[i].equals("-")) {
 654                    queryFileName = "-";
 655                    i++;
 656                } else {
 657                    badUsage(command, "Unknown option " + args[i]);
 658                }
 659            } else {
 660                break;
 661            }
 662        }
 663
 664        if (queryFileName == null) {
 665            if (args.length < i + 1) {
 666                badUsage(command, "No query file name");
 667            }
 668            queryFileName = args[i++];
 669        }
 670
 671        for (int p = i; p < args.length; p++) {
 672            String arg = args[p];
 673            int eq = arg.indexOf("=");
 674            if (eq < 1 || eq >= arg.length()) {
 675                badUsage(command, "Bad param=value pair on command line: " + arg);
 676            }
 677            String argname = arg.substring(0, eq);
 678            String argvalue = (eq == arg.length() ? "" : arg.substring(eq + 1));
 679            if (argname.startsWith("!")) {
 680                // parameters starting with "!" are taken as output properties
 681                outputProps.setProperty(argname.substring(1), argvalue);
 682            } else if (argname.startsWith("+")) {
 683                // parameters starting with "+" are taken as input documents
 684                Object sources = Transform.loadDocuments(argvalue, useURLs, config, true);
 685                dynamicEnv.setParameter(argname.substring(1), sources);
 686            } else {
 687                dynamicEnv.setParameter(argname, new UntypedAtomicValue(argvalue));
 688            }
 689        }
 690
 691        if (additionalSchemas != null) {
 692            loadAdditionalSchemas(config, additionalSchemas);
 693        }
 694    }
 695
 696    protected static void loadAdditionalSchemas(Configuration config, String additionalSchemas)
 697            throws TransformerException {
 698        StringTokenizer st = new StringTokenizer(additionalSchemas, ";");
 699        while (st.hasMoreTokens()) {
 700            String schema = st.nextToken();
 701            File schemaFile = new File(schema);
 702            if (!schemaFile.exists()) {
 703                throw new TransformerException("Schema document " + schema + " not found");
 704            }
 705            config.addSchemaSource(new StreamSource(schemaFile));
 706        }
 707    }
 708
 709    protected Source processSourceFile(String sourceFileName, boolean useURLs) throws TransformerException {
 710        Source sourceInput;
 711        if (useURLs || sourceFileName.startsWith("http:") || sourceFileName.startsWith("file:")) {
 712            sourceInput = config.getURIResolver().resolve(sourceFileName, null);
 713            if (sourceInput == null) {
 714                sourceInput = config.getSystemURIResolver().resolve(sourceFileName, null);
 715            }
 716        } else if (sourceFileName.equals("-")) {
 717            // take input from stdin
 718            sourceInput = new StreamSource(System.in);
 719        } else {
 720            File sourceFile = new File(sourceFileName);
 721            if (!sourceFile.exists()) {
 722                quit("Source file " + sourceFile + " does not exist", 2);
 723            }
 724
 725            if (Configuration.getPlatform().isJava()) {
 726                InputSource eis = new InputSource(sourceFile.toURI().toString());
 727                sourceInput = new SAXSource(eis);
 728            } else {
 729                sourceInput = new StreamSource(sourceFile.toURI().toString());
 730            }
 731        }
 732        return sourceInput;
 733    }
 734
 735    /**
 736     * Compile the query
 737     *
 738     * @param staticEnv     the static query context
 739     * @param queryFileName the filename holding the query (or "-" for the standard input)
 740     * @param useURLs       true if the filename is in the form of a URI
 741     * @return the compiled query
 742     * @throws XPathException if query compilation fails
 743     * @throws IOException    if the query cannot be read
 744     */
 745
 746    protected XQueryExpression compileQuery(StaticQueryContext staticEnv, String queryFileName, boolean useURLs)
 747            throws XPathException, IOException {
 748        XQueryExpression exp;
 749        if (queryFileName.equals("-")) {
 750            Reader queryReader = new InputStreamReader(System.in);
 751            exp = staticEnv.compileQuery(queryReader);
 752        } else if (queryFileName.startsWith("{") && queryFileName.endsWith("}")) {
 753            // query is inline on the command line
 754            String q = queryFileName.substring(1, queryFileName.length() - 1);
 755            exp = staticEnv.compileQuery(q);
 756        } else if (useURLs || queryFileName.startsWith("http:") || queryFileName.startsWith("file:")) {
 757            ModuleURIResolver resolver = staticEnv.getModuleURIResolver();
 758            boolean isStandardResolver = false;
 759            if (resolver == null) {
 760                resolver = staticEnv.getConfiguration().getStandardModuleURIResolver();
 761                isStandardResolver = true;
 762            }
 763            while (true) {
 764                String[] locations = {queryFileName};
 765                Source[] sources;
 766                try {
 767                    sources = resolver.resolve(null, null, locations);
 768                } catch (Exception e) {
 769                    if (e instanceof XPathException) {
 770                        throw (XPathException)e;
 771                    } else {
 772                        XPathException err = new XPathException("Exception in ModuleURIResolver: ", e);
 773                        err.setErrorCode("XQST0059");
 774                        throw err;
 775                    }
 776                }
 777                if (sources == null) {
 778                    if (isStandardResolver) {
 779                        // this should not happen
 780                        quit("System problem: standard ModuleURIResolver returned null", 4);
 781                    } else {
 782                        resolver = staticEnv.getConfiguration().getStandardModuleURIResolver();
 783                        isStandardResolver = true;
 784                    }
 785                } else {
 786                    if (sources.length != 1 || !(sources[0] instanceof StreamSource)) {
 787                        quit("Module URI Resolver must return a single StreamSource", 2);
 788                    }
 789                    String queryText = QueryReader.readSourceQuery((StreamSource)sources[0], config.getNameChecker());
 790                    exp = staticEnv.compileQuery(queryText);
 791                    break;
 792                }
 793            }
 794        } else {
 795            InputStream queryStream = new FileInputStream(queryFileName);
 796            staticEnv.setBaseURI(new File(queryFileName).toURI().toString());
 797            exp = staticEnv.compileQuery(queryStream, null);
 798        }
 799        return exp;
 800    }
 801
 802    /**
 803     * Explain the results of query compilation
 804     *
 805     * @param exp the compiled expression
 806     * @throws FileNotFoundException if the destination for the explanation doesn't exist
 807     * @throws XPathException        if other failures occur
 808     */
 809
 810    protected void explain(XQueryExpression exp) throws FileNotFoundException, XPathException {
 811        OutputStream explainOutput;
 812        if (explainOutputFileName == null) {
 813            explainOutput = System.err;
 814        } else {
 815            explainOutput = new FileOutputStream(new File(explainOutputFileName));
 816        }
 817        Properties props = new Properties();
 818        props.setProperty(OutputKeys.METHOD, "xml");
 819        props.setProperty(OutputKeys.INDENT, "yes");
 820        props.setProperty(SaxonOutputKeys.INDENT_SPACES, "2");
 821        Receiver diag = config.getSerializerFactory().getReceiver(
 822                new StreamResult(explainOutput),
 823                config.makePipelineConfiguration(),
 824                props);
 825        ExpressionPresenter expressionPresenter = new ExpressionPresenter(config, diag);
 826        exp.explain(expressionPresenter);
 827    }
 828
 829    /**
 830     * Process the supplied source file
 831     *
 832     * @param sourceInput the supplied source
 833     * @param exp         the compiled XQuery expression
 834     * @param dynamicEnv  the dynamic query context
 835     * @throws XPathException if processing fails
 836     */
 837
 838    protected void processSource(Source sourceInput, XQueryExpression exp, DynamicQueryContext dynamicEnv) throws XPathException {
 839        if (sourceInput != null) {
 840            if (showTime) {
 841                System.err.println("Processing " + sourceInput.getSystemId());
 842            }
 843            if (!exp.usesContextItem()) {
 844                System.err.println("Source document ignored - query does not access the context item");
 845                sourceInput = null;
 846
 847            } else if (projection) {
 848                PathMap map = exp.getPathMap();
 849                PathMap.PathMapRoot contextRoot = map.getContextRoot();
 850                if (explain) {
 851                    System.err.println("DOCUMENT PROJECTION: PATH MAP");
 852                    map.diagnosticDump(System.err);
 853                }
 854                if (contextRoot != null) {
 855                    if (contextRoot.hasUnknownDependencies()) {
 856                        System.err.println("Document projection for the context document is not possible, " +
 857                                "because the query uses paths that defy analysis");
 858                    } else {
 859                        ProxyReceiver filter = config.makeDocumentProjector(contextRoot);
 860                        sourceInput = AugmentedSource.makeAugmentedSource(sourceInput);
 861                        ((AugmentedSource)sourceInput).addFilter(filter);
 862                    }
 863                } else {
 864                    System.err.println("Source document supplied, but query does not access the context item");
 865                }
 866            }
 867            if (sourceInput != null) {
 868                DocumentInfo doc = config.buildDocument(sourceInput);
 869                dynamicEnv.setContextItem(doc);
 870            }
 871        }
 872    }
 873
 874    /**
 875     * Run the query
 876     *
 877     * @param exp         the compiled query expression
 878     * @param dynamicEnv  the dynamic query context
 879     * @param destination the destination for serialized results
 880     * @param outputProps serialization properties defining the output format
 881     * @throws XPathException if the query fails
 882     * @throws IOException    if input or output fails
 883     */
 884    protected void runQuery(XQueryExpression exp, DynamicQueryContext dynamicEnv,
 885                            OutputStream destination, final Properties outputProps)
 886            throws XPathException, IOException {
 887        if (exp.getExpression().isUpdatingExpression() && updating) {
 888
 889            if (writeback) {
 890                final List errors = new ArrayList(3);
 891                UpdateAgent agent = new UpdateAgent() {
 892                    public void update(NodeInfo node, Controller controller) throws XPathException {
 893                        try {
 894                            DocumentPool pool = controller.getDocumentPool();
 895                            String documentURI = pool.getDocumentURI(node);
 896                            if (documentURI != null) {
 897                                QueryResult.rewriteToDisk(node, outputProps, backup, (showTime ? System.err : null));
 898                            } else if (showTime) {
 899                                System.err.println("Updated document discarded because it was not read using doc()");
 900                            }
 901                        } catch (XPathException err) {
 902                            System.err.println(err.getMessage());
 903                            errors.add(err);
 904                        }
 905                    }
 906                };
 907                exp.runUpdate(dynamicEnv, agent);
 908
 909                if (!errors.isEmpty()) {
 910                    throw (XPathException)errors.get(0);
 911                }
 912            } else {
 913                Set affectedDocuments = exp.runUpdate(dynamicEnv);
 914                if (affectedDocuments.contains(dynamicEnv.getContextItem())) {
 915                    QueryResult.serialize((NodeInfo)dynamicEnv.getContextItem(),
 916                            new StreamResult(destination),
 917                            outputProps);
 918                }
 919            }
 920        } else if (wrap && !pullMode) {
 921            SequenceIterator results = exp.iterator(dynamicEnv);
 922            DocumentInfo resultDoc = QueryResult.wrap(results, config);
 923            QueryResult.serialize(resultDoc,
 924                    new StreamResult(destination),
 925                    outputProps);
 926            destination.close();
 927        } else if (pullMode) {
 928            if (wrap) {
 929                outputProps.setProperty(SaxonOutputKeys.WRAP, "yes");
 930            }
 931            //outputProps.setProperty(OutputKeys.METHOD, "xml");
 932            //outputProps.setProperty(OutputKeys.INDENT, "yes");
 933            exp.pull(dynamicEnv, new StreamResult(destination), outputProps);
 934        } else {
 935            exp.run(dynamicEnv, new StreamResult(destination), outputProps);
 936        }
 937        if (closeTraceDestination) {
 938            dynamicEnv.getTraceFunctionDestination().close();
 939        }                     
 940    }
 941
 942    /**
 943     * Exit with a message
 944     *
 945     * @param message The message to be output
 946     * @param code    The result code to be returned to the operating
 947     *                system shell
 948     */
 949
 950    protected static void quit(String message, int code) {
 951        System.err.println(message);
 952        System.exit(code);
 953    }
 954
 955//    public void setPOption(Configuration config) {
 956//        config.getSystemURIResolver().setRecognizeQueryParameters(true);
 957//    }
 958
 959    /**
 960     * Report incorrect usage of the command line, with a list of the options and arguments that are available
 961     *
 962     * @param name    The name of the command being executed (allows subclassing)
 963     * @param message The error message
 964     */
 965    protected void badUsage(String name, String message) {
 966        if (!"".equals(message)) {
 967            System.err.println(message);
 968        }
 969        System.err.println(config.getProductTitle());
 970        System.err.println("Usage: " + name + " [options] query {param=value}...");
 971        System.err.println("Options: ");
 972        System.err.println("  -backup:on|off        Save updated documents before overwriting");
 973        System.err.println("  -cr:classname         Use specified CollectionURIResolver class");
 974        System.err.println("  -dtd:on|off           Validate using DTD");
 975        System.err.println("  -expand:on|off        Expand defaults defined in schema/DTD");
 976        System.err.println("  -explain[:filename]   Display compiled expression tree");
 977        System.err.println("  -ext:[on|off]         Allow|Disallow external Java functions");
 978        System.err.println("  -l:on|off             Line numbering for source document");
 979        System.err.println("  -mr:classname         Use specified ModuleURIResolver class");
 980        System.err.println("  -o:filename           Send output to named file");
 981        System.err.println("  -outval:recover|fatal Handling of validation errors on result document");
 982        System.err.println("  -p                    Recognize Saxon file extensions and query parameters");
 983        System.err.println("  -pipe:push|pull       Execute internally in push or pull mode");
 984        System.err.println("  -projection:[on|off]  Use|Don't use source document projection");
 985        System.err.println("  -q:filename           Query file name");
 986        System.err.println("  -qs:string            Query string (usually in quotes)");
 987        System.err.println("  -r:classname          Use URIResolver class");
 988        System.err.println("  -repeat:N             Repeat N times for performance measurement");
 989        System.err.println("  -s:file|URI           Provide initial context document");
 990        System.err.println("  -sa                   Schema-aware query (requires Saxon-SA)");
 991        System.err.println("  -strip:all|none|ignorable      Strip whitespace text nodes");
 992        System.err.println("  -t                    Display version and timing information");
 993        System.err.println("  -traceout:file|#null  Destination for fn:trace() output");
 994        System.err.println("  -tree:tiny|linked     Select tree model");
 995        System.err.println("  -T[:classname]        Use TraceListener class");
 996        System.err.println("  -TJ                   Trace calls to external Java functions");
 997        System.err.println("  -u                    Names are URLs not filenames");
 998        System.err.println("  -update:on|off|discard  Enable|Disable XQuery Update (needs Saxon-SA)");
 999        System.err.println("  -val:strict|lax       Validate using schema");
1000        System.err.println("  -wrap:on|off          Wrap result sequence in XML elements");
1001        System.err.println("  -x:classname          Parser (XMLReader) used for source files");
1002        System.err.println("  -xi:on|off            Expand XInclude on all documents");
1003        System.err.println("  -xmlversion:1.0|1.1   Version of XML to be handled");
1004        System.err.println("  -xsd:file;file..      Additional schema documents to be loaded");
1005        System.err.println("  -xsdlversion:1.0|1.1  Version of XML Schema to be used");
1006        System.err.println("  -xsiloc:on|off        Take note of xsi:schemaLocation");
1007        System.err.println("  -?                    Display this message ");
1008        System.err.println("  param=value           Set query string parameter");
1009        System.err.println("  +param=value          Set query document parameter");
1010        System.err.println("  !option=value         Set serialization option");
1011        if ("".equals(message)) {
1012            System.exit(0);
1013        } else {
1014            System.exit(2);
1015        }
1016    }
1017
1018}
1019
1020//
1021// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1022// you may not use this file except in compliance with the License. You may obtain a copy of the
1023// License at http://www.mozilla.org/MPL/
1024//
1025// Software distributed under the License is distributed on an "AS IS" basis,
1026// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1027// See the License for the specific language governing rights and limitations under the License.
1028//
1029// The Original Code is: all this file.
1030//
1031// The Initial Developer of the Original Code is Michael H. Kay.
1032//
1033// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1034//
1035// Contributor(s): changes to allow source and/or stylesheet from stdin contributed by
1036// Gunther Schadow [gunther@aurora.regenstrief.org]
1037//