Esempio n. 1
0
  /**
   * Output usage message flag listing.
   *
   * @param subclassOpts array containing flag and description pairs
   */
  protected void outputFlags(String[][] subclassOpts) {

    // Build options list
    final ArrayList<String[]> optionList = new ArrayList<>();

    // Add options directly supported by AbstractMain
    optionList.addAll(
        Arrays.<String[]>asList(
            new String[][] {
              {"--classpath, -cp path", "Append to the classpath (useful with `java -jar ...')"},
              {"--read-only, -ro", "Disallow database modifications"},
              {"--new-schema", "Allow recording of a new database schema version"},
              {
                "--schema-version, -v num",
                "Specify database schema version (default highest recorded)"
              },
              {
                "--model-pkg package",
                "Scan for @JSimpleClass model classes under Java package (=> JSimpleDB mode)"
              },
              {
                "--type-pkg package",
                "Scan for @JFieldType types under Java package to register custom types"
              },
              {"--pkg, -p package", "Equivalent to `--model-pkg package --type-pkg package'"},
              {"--help, -h", "Show this help message"},
              {"--verbose", "Show verbose error messages"},
            }));

    // Add options supported by the various key/value implementations
    final KVImplementation[] kvs = KVImplementation.getImplementations();
    for (KVImplementation kv : kvs)
      optionList.addAll(Arrays.<String[]>asList(kv.getCommandLineOptions()));

    // Add options supported by subclass
    if (subclassOpts != null) optionList.addAll(Arrays.<String[]>asList(subclassOpts));

    // Sort options
    Collections.sort(
        optionList,
        new Comparator<String[]>() {
          @Override
          public int compare(String[] opt1, String[] opt2) {
            return opt1[0].compareTo(opt2[0]);
          }
        });

    // Display all supported options
    int width = 0;
    for (String[] opt : optionList) width = Math.max(width, opt[0].length());
    for (String[] opt : optionList)
      System.err.println(String.format("  %-" + width + "s  %s", opt[0], opt[1]));

    // Display additional usage text
    for (KVImplementation kv : kvs) {
      final String usageText = kv.getUsageText();
      if (usageText != null) System.err.println(usageText.trim());
    }
  }
Esempio n. 2
0
  /**
   * Parse command line options.
   *
   * @param params command line parameters
   * @return -1 to proceed, otherwise process exit value
   */
  public int parseOptions(ArrayDeque<String> params) {

    // Special logic to automate the demo when no flags are given
    if (params.isEmpty() && DEMO_XML_FILE.exists() && DEMO_SUBDIR.exists()) {
      params.add("--xml");
      params.add(DEMO_XML_FILE.toString());
      params.add("--classpath");
      params.add(DEMO_SUBDIR.toString());
      params.add("--model-pkg");
      params.add("org.jsimpledb.demo");
      final StringBuilder buf = new StringBuilder();
      for (String param : params) buf.append(' ').append(param);
      System.err.println(
          this.getName()
              + ": automatically configuring demo database using the following flags:\n "
              + buf);
    }

    // Parse --classpath options prior to searching classpath for key/value implementations
    for (Iterator<String> i = params.iterator(); i.hasNext(); ) {
      final String param = i.next();
      if (param.equals("-cp") || param.equals("--classpath")) {
        i.remove();
        if (!i.hasNext()) this.usageError();
        if (!this.appendClasspath(i.next())) return 1;
        i.remove();
      }
    }

    // Parse (and remove) options supported by key/value implementations
    for (KVImplementation availableKVImplementation : KVImplementation.getImplementations()) {
      final Object config;
      try {
        config = availableKVImplementation.parseCommandLineOptions(params);
      } catch (IllegalArgumentException e) {
        System.err.println(this.getName() + ": " + (e.getMessage() != null ? e.getMessage() : e));
        return 1;
      }
      if (config != null) this.kvConfigMap.put(availableKVImplementation, config);
    }

    // Parse options supported by this class
    final LinkedHashSet<String> modelPackages = new LinkedHashSet<>();
    final LinkedHashSet<String> typePackages = new LinkedHashSet<>();
    while (!params.isEmpty() && params.peekFirst().startsWith("-")) {
      final String option = params.removeFirst();
      if (option.equals("-h") || option.equals("--help")) {
        this.usageMessage();
        return 0;
      } else if (option.equals("-ro") || option.equals("--read-only")) this.readOnly = true;
      else if (option.equals("--verbose")) this.verbose = true;
      else if (option.equals("-v") || option.equals("--schema-version")) {
        if (params.isEmpty()) this.usageError();
        final String vstring = params.removeFirst();
        try {
          this.schemaVersion = Integer.parseInt(vstring);
          if (this.schemaVersion < 0)
            throw new IllegalArgumentException("schema version is negative");
        } catch (Exception e) {
          System.err.println(
              this.getName() + ": invalid schema version `" + vstring + "': " + e.getMessage());
          return 1;
        }
      } else if (option.equals("--model-pkg")) {
        if (params.isEmpty()) this.usageError();
        modelPackages.add(params.removeFirst());
      } else if (option.equals("--type-pkg")) {
        if (params.isEmpty()) this.usageError();
        typePackages.add(params.removeFirst());
      } else if (option.equals("-p") || option.equals("--pkg")) {
        if (params.isEmpty()) this.usageError();
        final String packageName = params.removeFirst();
        modelPackages.add(packageName);
        typePackages.add(packageName);
      } else if (option.equals("--new-schema")) this.allowNewSchema = true;
      else if (option.equals("--")) break;
      else if (!this.parseOption(option, params)) {
        System.err.println(this.getName() + ": unknown option `" + option + "'");
        this.usageError();
        return 1;
      }
    }

    // Decode what key/value implementations where specified and how they nest, if at all
    final Iterator<KVImplementation> i = this.kvConfigMap.keySet().iterator();
    switch (this.kvConfigMap.size()) {
      case 0:
        System.err.println(
            this.getName() + ": no key/value database specified; use one of `--arraydb', etc.");
        this.usageError();
        return 1;

      case 1:
        this.kvImplementation = i.next();
        final Object config = this.kvConfigMap.get(this.kvImplementation);
        if (this.kvImplementation.requiresAtomicKVStore(config)
            || this.kvImplementation.requiresKVDatabase(config)) {
          System.err.println(
              this.getName()
                  + ": "
                  + this.kvImplementation.getDescription(config)
                  + " requires the configuration of an underlying key/value technology; use one of `--arraydb', etc.");
          return 1;
        }
        break;

      case 2:

        // Put them in proper order: inner first, outer second
        final KVImplementation[] kvis = new KVImplementation[] {i.next(), i.next()};
        final Object[] configs =
            new Object[] {this.kvConfigMap.get(kvis[0]), this.kvConfigMap.get(kvis[1])};
        if (kvis[0].requiresAtomicKVStore(configs[0]) || kvis[0].requiresKVDatabase(configs[0])) {
          Collections.reverse(Arrays.asList(kvis));
          Collections.reverse(Arrays.asList(configs));
        }

        // Sanity check nesting requirements
        if ((kvis[0].requiresAtomicKVStore(configs[0]) || kvis[0].requiresKVDatabase(configs[0]))
            || !(kvis[1].requiresAtomicKVStore(configs[1])
                || kvis[1].requiresKVDatabase(configs[1]))) {
          System.err.println(
              this.getName()
                  + ": incompatible combination of "
                  + kvis[0].getDescription(configs[0])
                  + " and "
                  + kvis[1].getDescription(configs[1]));
          return 1;
        }

        // Nest them as required
        if (kvis[1].requiresAtomicKVStore(configs[1])) this.requiredAtomicKVStore = kvis[0];
        else this.requiredKVDatabase = kvis[0];
        this.kvImplementation = kvis[1];
        break;

      default:
        System.err.println(this.getName() + ": too many key/value store(s) specified");
        return 1;
    }

    // Scan for model and type classes
    final LinkedHashSet<String> emptyPackages = new LinkedHashSet<>();
    emptyPackages.addAll(modelPackages);
    emptyPackages.addAll(typePackages);
    for (String packageName : modelPackages) {
      if (this.scanModelClasses(packageName) > 0) emptyPackages.remove(packageName);
    }
    for (String packageName : typePackages) {
      if (this.scanTypeClasses(packageName) > 0) emptyPackages.remove(packageName);
    }

    // Warn if we didn't find anything
    for (String packageName : emptyPackages) {
      final boolean isModel = modelPackages.contains(packageName);
      final boolean isType = typePackages.contains(packageName);
      if (isModel && isType)
        this.log.warn(
            "no Java model or custom FieldType classes found under package `" + packageName + "'");
      else if (isModel)
        this.log.warn("no Java model classes found under package `" + packageName + "'");
      else this.log.warn("no custom FieldType classes found under package `" + packageName + "'");
    }

    // Done
    return -1;
  }