/** Set the configuration. This is designed to be overridden in a subclass */
 protected Configuration makeConfiguration(boolean schemaAware) {
   if (schemaAware) {
     config = Configuration.makeSchemaAwareConfiguration(null);
   } else {
     config = new Configuration();
     // In basic XQuery, all nodes are untyped when calling from the command line
     config.setAllNodesUntyped(true);
   }
   return config;
 }
  /**
   * Support method for main program. This support method can also be invoked from subclasses that
   * support the same command line interface
   *
   * @param args the command-line arguments
   * @param name name of the class, to be used in error messages
   */
  protected void doQuery(String args[], String name) {
    boolean showTime = false;
    int repeat = 1;
    String sourceFileName = null;
    String queryFileName = null;
    File sourceFile;
    File outputFile;
    boolean useURLs = false;
    String outputFileName = null;
    boolean explain = false;
    boolean wrap = false;
    boolean pullMode = false;

    boolean schemaAware = false;
    for (int i = 0; i < args.length; i++) {
      if (args[i].equals("-sa")) {
        schemaAware = true;
      } else if (args[i].equals("-val")) {
        schemaAware = true;
      } else if (args[i].equals("-vlax")) {
        schemaAware = true;
      } else if (args[i].equals("-p")) {
        schemaAware = true;
      }
    }

    config = makeConfiguration(schemaAware);
    config.setHostLanguage(Configuration.XQUERY);

    StaticQueryContext staticEnv = new StaticQueryContext(config);
    DynamicQueryContext dynamicEnv = new DynamicQueryContext(config);

    Properties outputProps = new Properties();

    // Check the command-line arguments.

    try {
      int i = 0;
      while (i < args.length) {

        if (args[i].charAt(0) == '-') {

          if (args[i].equals("-cr")) {
            i++;
            if (args.length < i + 1) {
              badUsage(name, "No output file name");
            }
            String crclass = args[i++];
            Object resolver = config.getInstance(crclass, null);
            if (!(resolver instanceof CollectionURIResolver)) {
              quit(crclass + " is not a CollectionURIResolver", 2);
            }
            config.setCollectionURIResolver((CollectionURIResolver) resolver);

          } else if (args[i].equals("-ds")) {
            config.setTreeModel(Builder.LINKED_TREE);
            i++;
          } else if (args[i].equals("-dt")) {
            config.setTreeModel(Builder.TINY_TREE);
            i++;
          } else if (args[i].equals("-e")) {
            explain = true;
            i++;
          } else if (args[i].equals("-l")) {
            config.setLineNumbering(true);
            i++;
          } else if (args[i].equals("-3")) { // undocumented option: do it thrice
            i++;
            repeat = 3;
          } else if (args[i].equals("-9")) { // undocumented option: do it nine times
            i++;
            repeat = 9;
          } else if (args[i].equals("-mr")) {
            i++;
            if (args.length < i + 1) {
              badUsage(name, "No ModuleURIResolver class");
            }
            String r = args[i++];
            config.setModuleURIResolver(r);
          } else if (args[i].equals("-noext")) {
            i++;
            config.setAllowExternalFunctions(false);
          } else if (args[i].equals("-o")) {
            i++;
            if (args.length < i + 1) {
              badUsage(name, "No output file name");
            }
            outputFileName = args[i++];
          } else if (args[i].equals("-p")) {
            i++;
            setPOption(config);
            useURLs = true;
          } else if (args[i].equals("-pull")) {
            i++;
            pullMode = true;
          } else if (args[i].equals("-r")) {
            i++;
            if (args.length < i + 1) {
              badUsage(name, "No URIResolver class");
            }
            String r = args[i++];
            config.setURIResolver(config.makeURIResolver(r));
            dynamicEnv.setURIResolver(config.makeURIResolver(r));
          } else if (args[i].equals("-s")) {
            i++;
            if (args.length < i + 1) {
              badUsage(name, "No source file name");
            }
            sourceFileName = args[i++];
          } else if (args[i].equals("-sa")) {
            // already handled
            i++;
          } else if (args[i].equals("-snone")) {
            config.setStripsWhiteSpace(Whitespace.NONE);
            i++;
          } else if (args[i].equals("-sall")) {
            config.setStripsWhiteSpace(Whitespace.ALL);
            i++;
          } else if (args[i].equals("-signorable")) {
            config.setStripsWhiteSpace(Whitespace.IGNORABLE);
            i++;
          } else if (args[i].equals("-strip")) { // retained for compatibility
            config.setStripsWhiteSpace(Whitespace.ALL);
            i++;
          } else if (args[i].equals("-t")) {
            System.err.println(config.getProductTitle());
            // System.err.println("Java version " + System.getProperty("java.version"));
            System.err.println(config.getPlatform().getPlatformVersion());
            config.setTiming(true);
            showTime = true;
            i++;
          } else if (args[i].equals("-T")) {
            config.setTraceListener(new XQueryTraceListener());
            i++;
          } else if (args[i].equals("-TJ")) {
            i++;
            config.setTraceExternalFunctions(true);
          } else if (args[i].equals("-TL")) {
            if (args.length < i + 2) {
              badUsage(name, "No TraceListener class specified");
            }
            TraceListener traceListener = config.makeTraceListener(args[++i]);
            config.setTraceListener(traceListener);
            config.setLineNumbering(true);
            i++;
          } else if (args[i].equals("-u")) {
            useURLs = true;
            i++;
          } else if (args[i].equals("-untyped")) {
            // TODO: this is an experimental undocumented option. It should be checked for
            // consistency
            config.setAllNodesUntyped(true);
            i++;
          } else if (args[i].equals("-v")) {
            config.setValidation(true);
            i++;
          } else if (args[i].equals("-val")) {
            if (schemaAware) {
              config.setSchemaValidationMode(Validation.STRICT);
            } else {
              quit("The -val option requires a schema-aware processor", 2);
            }
            i++;
          } else if (args[i].equals("-vlax")) {
            if (schemaAware) {
              config.setSchemaValidationMode(Validation.LAX);
            } else {
              quit("The -vlax option requires a schema-aware processor", 2);
            }
            i++;
          } else if (args[i].equals("-vw")) {
            if (schemaAware) {
              config.setValidationWarnings(true);
            } else {
              quit("The -vw option requires a schema-aware processor", 2);
            }
            i++;
          } else if (args[i].equals("-wrap")) {
            wrap = true;
            i++;
          } else if (args[i].equals("-1.1")) {
            config.setXMLVersion(Configuration.XML11);
            i++;
          } else if (args[i].equals("-?")) {
            badUsage(name, "");
          } else if (args[i].equals("-")) {
            queryFileName = "-";
            i++;
          } else {
            badUsage(name, "Unknown option " + args[i]);
          }
        } else {
          break;
        }
      }

      if (!("-".equals(queryFileName))) {
        if (args.length < i + 1) {
          badUsage(name, "No query file name");
        }
        queryFileName = args[i++];
      }

      for (int p = i; p < args.length; p++) {
        String arg = args[p];
        int eq = arg.indexOf("=");
        if (eq < 1 || eq >= arg.length() - 1) {
          badUsage(name, "Bad param=value pair on command line: " + arg);
        }
        String argname = arg.substring(0, eq);
        if (argname.startsWith("!")) {
          // parameters starting with "!" are taken as output properties
          outputProps.setProperty(argname.substring(1), arg.substring(eq + 1));
        } else if (argname.startsWith("+")) {
          // parameters starting with "+" are taken as input documents
          Object sources = Transform.loadDocuments(arg.substring(eq + 1), useURLs, config, true);
          dynamicEnv.setParameter(argname.substring(1), sources);
        } else {
          dynamicEnv.setParameter(argname, new UntypedAtomicValue(arg.substring(eq + 1)));
        }
      }

      config.displayLicenseMessage();
      if (pullMode) {
        config.setLazyConstructionMode(true);
      }

      Source sourceInput = null;

      if (sourceFileName != null) {
        if (useURLs || sourceFileName.startsWith("http:") || sourceFileName.startsWith("file:")) {
          sourceInput = config.getURIResolver().resolve(sourceFileName, null);
          if (sourceInput == null) {
            sourceInput = config.getSystemURIResolver().resolve(sourceFileName, null);
          }
        } else if (sourceFileName.equals("-")) {
          // take input from stdin
          sourceInput = new StreamSource(System.in);
        } else {
          sourceFile = new File(sourceFileName);
          if (!sourceFile.exists()) {
            quit("Source file " + sourceFile + " does not exist", 2);
          }

          if (config.getPlatform() instanceof JavaPlatform) {
            InputSource eis = new InputSource(sourceFile.toURI().toString());
            sourceInput = new SAXSource(eis);
          } else {
            sourceInput = new StreamSource(sourceFile.toURI().toString());
          }
        }
      }

      long startTime = (new Date()).getTime();
      if (showTime) {
        System.err.println("Compiling query from " + queryFileName);
      }

      XQueryExpression exp;

      try {
        if (queryFileName.equals("-")) {
          Reader queryReader = new InputStreamReader(System.in);
          exp = staticEnv.compileQuery(queryReader);
        } else if (queryFileName.startsWith("{") && queryFileName.endsWith("}")) {
          // query is inline on the command line
          String q = queryFileName.substring(1, queryFileName.length() - 1);
          exp = staticEnv.compileQuery(q);
        } else if (useURLs
            || queryFileName.startsWith("http:")
            || queryFileName.startsWith("file:")) {
          ModuleURIResolver resolver = config.getModuleURIResolver();
          String[] locations = {queryFileName};
          Source[] sources = resolver.resolve(null, null, locations);
          if (sources.length != 1 || !(sources[0] instanceof StreamSource)) {
            quit("Module URI Resolver must return a single StreamSource", 2);
          }
          String queryText =
              QueryReader.readSourceQuery((StreamSource) sources[0], config.getNameChecker());
          exp = staticEnv.compileQuery(queryText);
        } else {
          InputStream queryStream = new FileInputStream(queryFileName);
          staticEnv.setBaseURI(new File(queryFileName).toURI().toString());
          exp = staticEnv.compileQuery(queryStream, null);
        }
        staticEnv = exp.getStaticContext(); // the original staticContext is copied

        if (showTime) {
          long endTime = (new Date()).getTime();
          System.err.println("Compilation time: " + (endTime - startTime) + " milliseconds");
          startTime = endTime;
        }

      } catch (XPathException err) {
        int line = -1;
        String module = null;
        if (err.getLocator() != null) {
          line = err.getLocator().getLineNumber();
          module = err.getLocator().getSystemId();
        }
        if (err.hasBeenReported()) {
          quit("Failed to compile query", 2);
        } else {
          if (line == -1) {
            System.err.println("Failed to compile query: " + err.getMessage());
          } else {
            System.err.println("Static error at line " + line + " of " + module + ':');
            System.err.println(err.getMessage());
          }
        }
        exp = null;
        System.exit(2);
      }

      if (explain) {
        staticEnv.getExecutable().getKeyManager().explainKeys(config);
        staticEnv.explainGlobalVariables();
        staticEnv.explainGlobalFunctions();
        exp.explain(staticEnv.getConfiguration());
      }

      OutputStream destination;
      if (outputFileName != null) {
        outputFile = new File(outputFileName);
        if (outputFile.isDirectory()) {
          quit("Output is a directory", 2);
        }
        destination = new FileOutputStream(outputFile);
      } else {
        destination = System.out;
      }

      for (int r = 0; r < repeat; r++) { // repeat is for internal testing/timing

        if (sourceInput != null) {
          if (showTime) {
            System.err.println("Processing " + sourceInput.getSystemId());
          }
          DocumentInfo doc = staticEnv.buildDocument(sourceInput);
          dynamicEnv.setContextItem(doc);
        }

        try {
          if (wrap) {
            SequenceIterator results = exp.iterator(dynamicEnv);
            DocumentInfo resultDoc = QueryResult.wrap(results, config);
            QueryResult.serialize(resultDoc, new StreamResult(destination), outputProps, config);
            destination.close();
          } else if (pullMode) {
            if (wrap) {
              outputProps.setProperty(SaxonOutputKeys.WRAP, "yes");
            }
            try {
              exp.pull(dynamicEnv, new StreamResult(destination), outputProps);
            } catch (XPathException err) {
              config.reportFatalError(err);
              throw err;
            }
          } else {
            exp.run(dynamicEnv, new StreamResult(destination), outputProps);
          }
        } catch (TerminationException err) {
          throw err;
        } catch (XPathException err) {
          if (err.hasBeenReported()) {
            throw new DynamicError("Run-time errors were reported");
          } else {
            throw err;
          }
        }

        if (showTime) {
          long endTime = (new Date()).getTime();
          System.err.println("Execution time: " + (endTime - startTime) + " milliseconds");
          startTime = endTime;
        }
      }

    } catch (TerminationException err) {
      quit(err.getMessage(), 1);
    } catch (XPathException err) {
      quit("Query processing failed: " + err.getMessage(), 2);
    } catch (TransformerFactoryConfigurationError err) {
      err.printStackTrace();
      quit("Query processing failed", 2);
    } catch (Exception err2) {
      err2.printStackTrace();
      quit("Fatal error during transformation: " + err2.getMessage(), 2);
    }
  }