kohsuke/args4j

Add a possibility for partial arguments parsing

highsource opened this issue · 0 comments

I'd like to use args4j in XJC plugins.

They may implement a method like:

public int parseArgument( Options opt, String[] args, int i ) throws BadCommandLineException, IOException {
        return 0;
    }

i is initial position the the arguments list and return value is the number of the arguments consumed.

So I basically need the something similar in the CmdLineParser. This is how I did it at the moment:

    public int parseArgument(final String[] args, final int position)
            throws CmdLineException {
        Validate.noNullElements(args);
        currentOptionHandler = null;

        CmdLineImpl cmdLine = new CmdLineImpl(args, position);

        Set<OptionHandler<?>> present = new HashSet<OptionHandler<?>>();
        int argIndex = position;
        int consumed = 0;

        while (cmdLine.hasMore()) {
            String arg = cmdLine.getCurrentToken();
            if (isOption(arg)) {
                // '=' is for historical compatibility fallback
                boolean isKeyValuePair = arg.contains(getProperties()
                        .getOptionValueDelimiter()) || arg.indexOf('=') != -1;

                // parse this as an option.
                currentOptionHandler = isKeyValuePair ? findOptionHandler(arg)
                        : findOptionByName(arg);

                if (currentOptionHandler == null) {
                    return consumed;
                }

                // known option; skip its name
                if (isKeyValuePair) {
                    cmdLine.splitToken();
                } else {
                    cmdLine.proceed(1);
                    consumed++;
                }
            } else {
                if (argIndex >= getArguments().size()) {
                    Messages msg = getArguments().size() == 0 ? Messages.NO_ARGUMENT_ALLOWED
                            : Messages.TOO_MANY_ARGUMENTS;
                    throw new CmdLineException(this, msg, arg);
                }

                // known argument
                currentOptionHandler = getArguments().get(argIndex);
                if (currentOptionHandler == null) // this is a programmer error.
                                                    // arg index should be
                                                    // continuous
                    throw new IllegalStateException("@Argument with index="
                            + argIndex + " is undefined");

                if (!currentOptionHandler.option.isMultiValued())
                    argIndex++;
            }
            int diff = currentOptionHandler.parseArguments(cmdLine);
            cmdLine.proceed(diff);
            consumed += diff;
            present.add(currentOptionHandler);
        }

        // check whether a help option is set
        boolean helpSet = false;
        for (OptionHandler<?> handler : getOptions()) {
            if (handler.option.help() && present.contains(handler)) {
                helpSet = true;
            }
        }

        if (!helpSet) {
            checkRequiredOptionsAndArguments(present);
        }

        return consumed;
    }

This is basically a copy-paste from CmdLineParser with the main difference that if some option can't be parsed, the method returns instead of throwing the exception.

Currently I've solved it by subclassing the CmdLineParser. I had to copy-paste a number of things as they are private/package-protected:

  • findOptionHandler
  • findOptionByName
  • checkRequiredOptionsAndArguments
    • isHandlerHasHisOptions
    • isHandlerAllowOtherOptions
  • `CmdLineImpl - also need a new constructor to set the initial position
    • getOptionName
  • org.kohsuke.args4j.Messages

Would you consider a PR for this functionality? Should I do one?