Ejemplo n.º 1
0
  public void parse(
      final boolean dryrun, final boolean detectHelpAndSkipValidation, String... cmdline) {
    if (log.isDebugEnabled()) {
      log.debug(
          "About to start parsing. dryrun: "
              + dryrun
              + ", detectHelpAndSkipValidation: "
              + detectHelpAndSkipValidation
              + ", state: "
              + debugState("  "));
    }

    if (defaultCommandName != null && !quickCommandMap.containsKey(defaultCommandName)) {
      final PreparedI18n msg =
          i18n.preparetr("Default command \"{0}\" is not a known command.", defaultCommandName);
      throw new CmdlineParserException(msg.notr(), msg.tr());
    }

    // Avoid null access
    cmdline = cmdline == null ? new String[] {} : cmdline;

    if (argsFromFilePrefix.isDefined()) {
      cmdline =
          FList.flatMap(
                  cmdline,
                  new F1<String, List<String>>() {
                    @Override
                    public List<String> apply(final String arg) {
                      if (arg.startsWith(argsFromFilePrefix.get())) {
                        debug("Expanding {0} into argument list", arg);
                        final File file = new File(arg.substring(1));
                        if (file.exists() && file.isFile()) {
                          try {
                            final BufferedReader reader = new BufferedReader(new FileReader(file));
                            final List<String> args = new LinkedList<String>();
                            String line;
                            while ((line = reader.readLine()) != null) {
                              // if (line.trim().length() > 0) {
                              args.add(line);
                              // }
                            }
                            reader.close();
                            return args;
                          } catch (final FileNotFoundException e) {
                            final PreparedI18n msg =
                                i18n.preparetr("File referenced via {0} does not exist.", arg);
                            throw new CmdlineParserException(msg.notr(), e, msg.tr());
                          } catch (final IOException e) {
                            final PreparedI18n msg =
                                i18n.preparetr("File referenced via {0} could not be read.", arg);
                            throw new CmdlineParserException(msg.notr(), e, msg.tr());
                          }
                        } else {
                          final PreparedI18n msg =
                              i18n.preparetr("File referenced via {0} does not exist.", arg);
                          throw new CmdlineParserException(msg.notr(), msg.tr());
                        }
                      } else {
                        return Arrays.asList(arg);
                      }
                    }
                  })
              .toArray(new String[0]);
    }

    if (!dryrun) {
      debug("Parsing...");
      // Check without applying anything
      parse(true, detectHelpAndSkipValidation, cmdline);
    }

    if (dryrun) {
      validateOptions();
    }

    // Should be set to false, if an stopOption was found and parsing of
    // options is no longer allowed
    boolean parseOptions = true;
    final String stopOption = "--";

    // optionCount counts the occurrence for each option handle in the
    // cmdline
    final Map<OptionHandle, Integer> optionCount = new LinkedHashMap<OptionHandle, Integer>();
    for (final OptionHandle option : options) {
      optionCount.put(option, 0);
    }
    if (parameter != null) {
      optionCount.put(parameter, 0);
    }

    boolean helpDetected = false;

    // Actually iterate over the command line elements
    for (int index = 0; index < cmdline.length; ++index) {
      final String param = cmdline[index];
      if (parseOptions && stopOption.equals(param)) {
        parseOptions = false;

      } else if (debugAllowed && param.equals("--CMDOPTION_DEBUG")) {
        if (!debugMode) {
          debugMode = true;
          debug("Enabled debug mode\n" + debugState(""));
        }

      } else if (parseOptions && quickOptionMap.containsKey(param)) {
        // Found an option
        final OptionHandle optionHandle = quickOptionMap.get(param);
        optionCount.put(optionHandle, optionCount.get(optionHandle) + 1);
        if (optionHandle.isHelp()) {
          debug("Detected a help request through: " + param);
          helpDetected = true;
        }

        if (cmdline.length <= index + optionHandle.getArgsCount()) {
          final PreparedI18n msg =
              i18n.preparetr(
                  "Missing arguments(s): {0}. Option \"{1}\" requires {2} arguments, but you gave {3}.",
                  FList.mkString(
                      Arrays.asList(optionHandle.getArgs())
                          .subList(cmdline.length - index - 1, optionHandle.getArgsCount()),
                      ", "),
                  param,
                  optionHandle.getArgsCount(),
                  cmdline.length - index - 1);
          throw new CmdlineParserException(msg.notr(), msg.tr());
        }
        // slurp next cmdline arguments into option arguments
        final String[] optionArgs =
            Arrays.copyOfRange(cmdline, index + 1, index + 1 + optionHandle.getArgsCount());
        index += optionHandle.getArgsCount();

        final AccessibleObject element = optionHandle.getElement();
        final CmdOptionHandler handler = optionHandle.getCmdOptionHandler();

        if (!dryrun) {
          try {
            final boolean origAccessibleFlag = element.isAccessible();
            if (!origAccessibleFlag) {
              element.setAccessible(true);
            }
            handler.applyParams(optionHandle.getObject(), element, optionArgs, param);
            if (!origAccessibleFlag) {
              // do not leave doors open
              element.setAccessible(origAccessibleFlag);
            }
          } catch (final CmdOptionHandlerException e) {
            throw new CmdlineParserException(e.getMessage(), e, e.getLocalizedMessage());
          } catch (final Exception e) {
            final PreparedI18n msg =
                i18n.preparetr(
                    "Could not apply parameters {0} to field/method {1}",
                    Arrays.toString(optionArgs), element);
            throw new CmdlineParserException(msg.notr(), e, msg.tr());
          }
        }
      } else if (parseOptions && quickCommandMap.containsKey(param)) {
        // Found a command
        final CommandHandle commandHandle = quickCommandMap.get(param);
        if (!dryrun) {
          parsedCommandName = param;
        }
        // Delegate parsing of the rest of the cmdline to the command
        commandHandle
            .getCmdlineParser()
            .parse(
                dryrun,
                detectHelpAndSkipValidation,
                Arrays.copyOfRange(cmdline, index + 1, cmdline.length));
        // Stop parsing
        break;

      } else if (parameter == null
          && defaultCommandName != null
          && quickCommandMap.containsKey(defaultCommandName)) {
        // Assume a default command inserted here
        debug(
            "Unsupported option '"
                + param
                + "' found, assuming default command: "
                + defaultCommandName);
        final CommandHandle commandHandle = quickCommandMap.get(defaultCommandName);

        if (!dryrun) {
          parsedCommandName = defaultCommandName;
        }
        // Delegate parsing of the rest of the cmdline to the command
        commandHandle
            .getCmdlineParser()
            .parse(
                dryrun,
                detectHelpAndSkipValidation,
                Arrays.copyOfRange(cmdline, index, cmdline.length));
        // Stop parsing
        break;

      } else if (parameter != null) {
        // Found a parameter
        optionCount.put(parameter, optionCount.get(parameter) + 1);

        if (cmdline.length <= index + parameter.getArgsCount() - 1) {
          final int countOfGivenParams = cmdline.length - index;
          final PreparedI18n msg =
              i18n.preparetr(
                  "Missing arguments: {0} Parameter requires {1} arguments, but you gave {2}.",
                  Arrays.asList(parameter.getArgs())
                      .subList(countOfGivenParams, parameter.getArgsCount()),
                  parameter.getArgsCount(),
                  countOfGivenParams);
          throw new CmdlineParserException(msg.notr(), msg.tr());
        }
        // slurp next cmdline arguments into option arguments
        final String[] optionArgs =
            Arrays.copyOfRange(cmdline, index, index + parameter.getArgsCount());
        // -1, because index gets increased by one at end of for-loop
        index += parameter.getArgsCount() - 1;

        final AccessibleObject element = parameter.getElement();
        final CmdOptionHandler handler = parameter.getCmdOptionHandler();

        if (!dryrun) {
          try {
            debug("Apply main parameter from parameters: {0}", FList.mkString(optionArgs, ", "));
            final boolean origAccessibleFlag = element.isAccessible();
            if (!origAccessibleFlag) {
              element.setAccessible(true);
            }
            handler.applyParams(parameter.getObject(), element, optionArgs, param);
            if (!origAccessibleFlag) {
              // do not leave doors open
              element.setAccessible(origAccessibleFlag);
            }
          } catch (final CmdOptionHandlerException e) {
            throw new CmdlineParserException(e.getMessage(), e, e.getLocalizedMessage());
          } catch (final Exception e) {
            final PreparedI18n msg =
                i18n.preparetr(
                    "Could not apply parameters {0} to field/method {1}",
                    Arrays.toString(optionArgs), element);
            throw new CmdlineParserException(msg.notr(), e, msg.tr());
          }
        }

      } else {
        final PreparedI18n msg =
            i18n.preparetr("Unsupported option or parameter found: {0}", param);
        throw new CmdlineParserException(msg.notr(), msg.tr());
      }
    }

    if (!detectHelpAndSkipValidation || !helpDetected) {
      // Validate optionCount matches allowed
      for (final Entry<OptionHandle, Integer> optionC : optionCount.entrySet()) {
        final OptionHandle option = optionC.getKey();
        final Integer count = optionC.getValue();
        if (count < option.getMinCount()
            || (option.getMaxCount() > 0 && count > option.getMaxCount())) {
          final PreparedI18n rangeMsg;
          if (option.getMaxCount() < 0) {
            rangeMsg = i18n.preparetr("at least {0}", option.getMinCount());
          } else {
            if (option.getMinCount() == option.getMaxCount()) {
              rangeMsg = i18n.preparetr("exactly {0}", option.getMinCount());
            } else {
              rangeMsg =
                  i18n.preparetr("between {0} and {1}", option.getMinCount(), option.getMaxCount());
            }
          }
          final String msg;
          final Object[] msgArgs;
          final Object[] msgArgsTr;
          if (option.getNames() == null || option.getNames().length == 0) {
            msg =
                I18n.marktr(
                    "Main parameter \"{0}\" was given {1} times, but must be given {2} times");
            msgArgs = new Object[] {FList.mkString(option.getArgs(), " "), count, rangeMsg.notr()};
            msgArgsTr = new Object[] {FList.mkString(option.getArgs(), " "), count, rangeMsg.tr()};
          } else {
            msg = I18n.marktr("Option \"{0}\" was given {1} times, but must be given {2} times");
            msgArgs = new Object[] {option.getNames()[0], count, rangeMsg.notr()};
            msgArgsTr = new Object[] {option.getNames()[0], count, rangeMsg.tr()};
          }
          throw new CmdlineParserException(
              MessageFormat.format(msg, msgArgs), i18n.tr(msg, msgArgsTr));
        }
      }

      // Validate required options because of 'required' attribute in
      // other options
      for (final Entry<OptionHandle, Integer> optionC : optionCount.entrySet()) {
        if (optionC.getValue() > 0) {
          final OptionHandle calledOption = optionC.getKey();
          for (final String required : calledOption.getRequires()) {
            // check, of an option was called with that name, if
            // not, this is an error
            final OptionHandle reqOptionHandle = quickOptionMap.get(required);
            if (reqOptionHandle == null) {
              // required option does not exists, error
              // TODO: error

            } else {
              final Integer reqOptionCount = optionCount.get(reqOptionHandle);
              if (reqOptionCount == null || reqOptionCount.intValue() <= 0) {
                // required option was not called, this is an
                // error
                final PreparedI18n msg =
                    i18n.preparetr(
                        "When using option \"{0}\" also option \"{1}\" must be given.",
                        calledOption.getNames()[0], required);
                throw new CmdlineParserException(msg.notr(), msg.tr());
              }
            }
          }
          for (final String conflict : calledOption.getConflictsWith()) {
            // check, of an option was called with that name, if
            // not, this is an error
            final OptionHandle conflictOptionHandle = quickOptionMap.get(conflict);
            if (conflictOptionHandle == null) {
              // conflicting option does not exists, error
              // TODO: error

            } else {
              final Integer conflictOptionCount = optionCount.get(conflictOptionHandle);
              if (conflictOptionCount != null && conflictOptionCount.intValue() > 0) {
                // conflicting option was called, this is an
                // conflict
                final PreparedI18n msg =
                    i18n.preparetr(
                        "Options \"{0}\" and \"{1}\" cannot be used at the same time.",
                        calledOption.getNames()[0], conflict);
                throw new CmdlineParserException(msg.notr(), msg.tr());
              }
            }
          }
        }
      }
    }
  }