GithubHelp home page GithubHelp logo

args4j's People

Contributors

basil avatar caniszczyk avatar cyrille-leclerc avatar daniel-beck avatar dantuch avatar davido avatar dlad avatar dnozay avatar douglarek avatar ebourg avatar fhuberts avatar gagarski avatar gaul avatar grossws avatar haumacher avatar javaerb avatar johnkeeping avatar kengotoda avatar kohsuke avatar lacostej avatar leanto avatar madkrupt avatar mikematrix avatar mrkozmic avatar msrb avatar nicolasgeraud avatar olivergondza avatar oluies avatar teiesti avatar thomas-mc-work avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

args4j's Issues

-h should take priority if present

When some parameters are provided together with [-h|-help], the program will execute instead of printing the helper message.

The desired behavior from my perspective is when [-h|-help] is presented, directly print the helper message.

ArrayIndexOutOfBoundsException while trying to create CmdLineException for malformed Argument value.

While parsing an argument of type int and supplying any string (or any non valid input) as the only parameter supplied, it will throw this:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
    at org.kohsuke.args4j.CmdLineParser$CmdLineImpl.getParameter(CmdLineParser.java:427)
    at org.kohsuke.args4j.spi.OneArgumentOptionHandler.parseArguments(OneArgumentOptionHandler.java:32)
    at org.kohsuke.args4j.CmdLineParser.parseArgument(CmdLineParser.java:490)

Reason is, it tried to generate a CmdLineException in OneArgumentOptionHandler.parseArguments() that requests the previous index. Since it does not have a option tag or anything and the option itself is index 0, it will try and retrieve index -1, which will cause this problem.

I'm using 2.0.25.

Add a possibility for partial arguments parsing

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?

Make expandAtFiles protected

It'd be nice if expandAtFiles was protected so that we override how arguments are loaded from the given @ files. Maybe even have a protected List<String> readAtFile(File f) to make that easier.

Issue with custom OptionHandler that shall assign a field of type "java.util.Properties"

Hi All,
I was trying to implement an OptionHandler that shall assing a field of type "java.util.Properties". Imagine a properties class as follows:

public class CommandLineOptions {

    @Option(name = "--account", usage = "The file to read the account properties from", required = true, handler = PropertiesFileOptionHandler.class)
    public Properties accountProperties;

}

The PropertiesFileOptionHandler.java file is as follows:

public class PropertiesFileOptionHandler extends OptionHandler<Properties> {

    public PropertiesFileOptionHandler(CmdLineParser parser, OptionDef option, Setter<? super Properties> setter) {
        super(parser, option, setter);
    }

    @Override
    public int parseArguments(final Parameters parameters) throws CmdLineException {
        Properties properties = new Properties();
        String propertiesFileName = parameters.getParameter(0);

        try {
            File propertiesFile = new File(propertiesFileName);

            if (!propertiesFile.isFile())
                throw new CmdLineException(this.owner, "File [" + propertiesFileName + "] not found");

            FileReader fileReader = new FileReader(propertiesFile);
            properties.load(fileReader);

        } catch (FileNotFoundException e) {
            throw new CmdLineException(this.owner, "File [" + propertiesFileName + "] not found");
        } catch (IOException e) {
            throw new CmdLineException(this.owner, "Unable to parse properties from (existing) file [" + propertiesFileName + "].");
        }

        setter.addValue(properties);
        return 1;
    }

    @Override
    public String getDefaultMetaVariable() {
        return "FILE";
    }

}

Running my program yields the following exception:

Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.util.Properties field test.CommandLineOptions.accountProperties to java.util.HashMap
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
    at java.lang.reflect.Field.set(Field.java:680)
    at org.kohsuke.args4j.MapSetter.addValue(MapSetter.java:60)
    at org.kohsuke.args4j.MapSetter.addValue(MapSetter.java:42)
    at test.util.args4j.PropertiesFileOptionHandler.parseArguments(PropertiesFileOptionHandler.java:45)
    at org.kohsuke.args4j.CmdLineParser.parseArgument(CmdLineParser.java:435)
    at test.Main.parseCmdLineOptions(Main.java:55)
    at test.Main.main(Main.java:27)

I tracked this down to org.kohsuke.args4j.spi.Setters where the following code is executed:

 public static Setter create(Field f, Object bean) {
        if(List.class.isAssignableFrom(f.getType()))
            return new MultiValueFieldSetter(bean,f);
        else if(Map.class.isAssignableFrom(f.getType()))
            return new MapSetter(bean,f);
        else
            return new FieldSetter(bean,f);
    }

and a MapSetter instance is created. However, I guess that the intended behavior in this case would be to create a FieldSetter instance.

One of given options is required

In some cases it would be useful to have an ability to define that one from a given list of commands is required (something like required for a group).

Error message for Enums does not contain the Argument name

When parsing an Enum as argument and specifying an invalid argument (one not contained in the allowed Enum values) args4j prints a message like this:

foo" is not a valid value for ""

What does nit for "" actually mean? Is it not supposed to contain the name of Parameter there? I would like to give this message back to the user to inform him about which value is not valid, but the above message does not supply any hints for that.

Don't use default meta variable for resource bundle lookup?

OptionHandler#getMetaVariable(...) uses the default meta variable as a resource bundle key, see lines 79-80. This is somewhat unlikely to be the correct choice, I'd rather expect that the default meta variable is not subject to localization.

For example the default meta variable for enums is the concatenation of the string representation of individual enum constants. This enum:

enum OptionEnum {
  OptionA, OptionB, OptionC
}

requires users to use the resource bundle key [OptionA\ |\ OptionB\ |\ OptionC]. And it's not even possible to omit that bundle key due to issue #70.

Does multiValued require JavaBean implementation?

I commonly use Args4J annotations on my "set argument" methods. For example:

@Option(name = "-numdays", usage = "number of days up to and including lastdate to process", required = false)
public void setNumDays(int numDays) {
    _numDays = numDays;
}

I'd like to use the multiValued=true qualifier in the same manner, but although the following compiles...

@Option(name = "-importdir", usage = "directory containing crawl info to import", required = true, multiValued = true)
public void setImportDirs(List<String> importDirs) {
    _importDirs = importDirs;
}

...I get an error at runtime when I try to specify this argument multiple times (e.g., "-importdir dir1 -importdir dir2"):

org.kohsuke.args4j.IllegalAnnotationError: No OptionHandler is registered to handle interface java.util.List

I note that MultivaluedTest applies the annotation to the instance variable directly. Is this the only way to make use of the multiValued qualifier, or have I made some other egregious error in my implementation?

Parser prints incorrect default values

My Arguments class contains a boolean help field, which should just print the usage and exit. However, when using help the default value is reported to be true instead of false.

Arguments:

@Option(name="--help", usage="print options and exit")
private boolean help = false;

Main:

Arguments argObj = new Arguments();
CmdLineParser parser = new CmdLineParser(argObj);
try {
    parser.parseArgument(args);
    if (argObj.help) {
        parser.printUsage(System.out);
        return;
    }
    // do stuff
} catch (CmdLineException e) {
    // handling of wrong arguments
    System.err.println(e.getMessage());
    parser.printUsage(System.err);
}

Unit Test:

@Test
public void testHelp() {
    PrintStream out = System.out;
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(baos);
        System.setOut(ps);
        // run help
        Main.main(new String[] { "--help" });
        CmdLineParser parser = new CmdLineParser(new Arguments());

        // get the usage
        ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
        PrintStream ps2 = new PrintStream(baos2);
        parser.printUsage(ps2);

        // make sure the only thing that's printed to the stdout is the
        // usage
        assertEquals(new String(baos.toByteArray()),
                new String(baos2.toByteArray()));
    } finally {
        System.setOut(out);
    }
}

testHelp FAILED
org.junit.ComparisonFailure: expected:<... and exit (default: [tru]e) (...> but was:<... and exit (default: [fals]e)
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)
at main.TestMain.testHelp(TestMain.java:36)

Starting index too late results in non-verbose NullPointerException

First of all: Thanks for making available this great CLI library!

When starting the arguments with index=1 instead of index=0 (or other, too high values) the framework presents are pretty non-verbose NullPointerException that gives no hint about the real cause:

Exception in thread "main" java.lang.NullPointerException
    at org.kohsuke.args4j.CmdLineParser.parseArgument(CmdLineParser.java:434)
    at de.unistuttgart.iste.se.sifcli.args4j.Main.main(Main.java:19)

This can be easily reproduced by parsing i.e. this code with the CommandLineParser:

    @Argument(required = true, index = 1, usage = "which of the favorite words do you prefer?")
    private ExptOption SomeOption;

    /** Valid options for the experiment type */
    public static enum SomeOption {
        FOO,
        BAR
    }

Enable dynamic construction of sub command list

When using a modular environment it might be desirable to register available commands using mechanisms such as OSGi service registrations or Guice multibinding injections. This is presently not possible since there's no way of creating a SubcommandHandler with a list that is obtained at runtime.

Please add a SubCommandHandler constructor where the list of SubCommands can be passed in as an argument.

maven plugin crashes when dealing with classes depending on other annotation processors

My main classes which use args4j are also annotated with some lombok annotations (like Slf4j. When args4j-maven processes such a file it crashes with a CCE:

java.lang.ClassCastException: com.sun.tools.apt.mirror.type.ClassTypeImpl cannot be cast to com.sun.mirror.type.AnnotationType
at com.sun.tools.apt.mirror.declaration.AnnotationMirrorImpl.getAnnotationType(AnnotationMirrorImpl.java:82)

When calling args4j-tools on the command line I do have the same behaviour.
After adding lombok.jar to the -cp args4j-tools works correctly.

I am not sure how to fix this problem, it seems that all compile-time dependencies should be added to the classpath when calling args4j-tools since they all could contain annotation processors.

Tested with args4j-tools-2.0.23-all.jar using jdk1.5.0_22 and jdk1.6.0_45

unwanted colon in second line in auto line break

When the usage value for an @option gets too long then args4j correctly automatically breaks it into two lines. But after the second line there is another colon (acting as a seperator between the usage value and the metaVar value.

Example:

@Option(name = "-e", aliases = "--environment", usage = "application environment (default: development)",
            metaVar = "training, production, testing, development, custom")

Output:

"xy" is not a valid value for "-e"
 -e (--environment) training, productio : application environment (default:
 n, testing, development, custom        : development)

Possibility to exclude Options if other option is present

Essentially exactly like Option.depends, but instead of enforcing the options to be present, enforcing them to be absent, so that it would be easily possible for Options to define which options should not be evaluated together.
This could be useful for things like --help or similar.

Building args4j-tools: Shade plugin generates error: Error creating shaded jar: access denied

When attempting to build the args4j-tools from source library for auto documentation creation, the maven shade plugin has problems accessing the "args4j-tools\target\classes" directory:

Maven Version: Apache Maven 3.3.1 (cab6659f9874fa96462afef40fcf6bc033d58c1c; 2015-03-13T14:10:27-06:00)
Maven home: C:\dev\lib\apache-maven-3.3.1\bin..
Java version: 1.8.0_51, vendor: Oracle Corporation
Java home: C:\dev\java\jdk1.8.0_51\jre
Default locale: en_US, platform encoding: Cp1252
OS name: "windows 7", version: "6.1", arch: "amd64", family: "dos"

Command Executed: C:\dev\src\OpenSourceProjects\java\koshuke-args4j>mvn package

[INFO] --- maven-shade-plugin:1.4:shade (default) @ args4j-tools ---
[INFO] Including args4j:args4j:jar:2.32 in the shaded jar.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.806 s
[INFO] Finished at: 2015-07-28T13:39:58-06:00
[INFO] Final Memory: 21M/274M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:1.4:shade (default) on project args4j-tools: Error creating shaded jar: C:\dev\src\OpenSourceProjects\java\koshuke-args4j\args4j-tools\target\classes (Access is denied) > [Help 1]

Is this similar to the problem outlined below?
Pi4J/pi4j-v1#78

Re-ran Maven with -X for debug info:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:1.4:shade (default) on project args4j-tools: Error creating shaded jar: C:\dev\src\OpenSourceProjects\java\koshuke-args4j\args4j-tools\target\classes (Access is denied) -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-
shade-plugin:1.4:shade (default) on project args4j-tools: Error creating shaded jar: C:\dev\src\OpenSourceProjects\java\koshuke-args4j\args4j-tools\target\classes (Access is denied)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:216)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
        at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions(MojoExecutor.java:364)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:198)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
        at org.apache.maven.cli.MavenCli.execute(MavenCli.java:862)
        at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:286)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:197)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoExecutionException: Error creating shaded jar: C:\dev\src\OpenSourceProjects\java\koshuke-args4j\args4j-tools\target\classes (Access is denied)
        at org.apache.maven.plugins.shade.mojo.ShadeMojo.execute(ShadeMojo.java:503)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
        ... 24 more
Caused by: java.io.FileNotFoundException: C:\dev\src\OpenSourceProjects\java\koshuke-args4j\args4j-tools\target\classes (Access is denied)
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.<init>(ZipFile.java:220)
        at java.util.zip.ZipFile.<init>(ZipFile.java:150)
        at java.util.jar.JarFile.<init>(JarFile.java:166)
        at java.util.jar.JarFile.<init>(JarFile.java:130)
        at org.apache.maven.plugins.shade.DefaultShader.newJarFile(DefaultShader.java:195)
        at org.apache.maven.plugins.shade.DefaultShader.shade(DefaultShader.java:86)
        at org.apache.maven.plugins.shade.mojo.ShadeMojo.execute(ShadeMojo.java:444)
        ... 26 more
[ERROR]
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

clearly document maven artefact co-ordinates

A section on the proper maven co-ordinates would help. I've spent some time looking and still haven't found the right ones!
...
Ok, I get it by looking under the source code in the args4j/pom.xml folder. But it could be a lot easier!

Print SubCommand-specific usage messages

CmdLineParser.getArguments() returns an OptionHandler for each Argument, including those used to define sub-commands, but there is no access to the SubCommand objects from the Argument. Make it so that if an Argument has associated sub-commands, the usage message for these is also printed using the various cli.printUsage(..) methods.

add release notes file

Add a file with a short description of the bugs fixed/features added for each release... either in the repo or the website.

Can't specify an option without the hyphen

Maybe it's simply not supported but can I specify an option without the hyphen? I have a program foo and I would like to call methods like :

foo sync
foo get X
foo push X
foo update
foo replace X Y

Are those possible with args4j? The way I see it right now I have to use arguments with this but I'm not sure it's that simple...

Thank you!

boolean option cannot have value

If I use the below code, I cannot pass "-v true" from command line since "true" will be parsed as an option but not a value for an option. This is largely due to the implementation of BooleanOptionHandler and NamedOptionDef. However, String option doesn't have this problem. Is this a bug or something by design? Cannot we make the two types consistent?

@Option(name = "-v", usage = "enable/disable validation mode")
private boolean validation = false;

forbids and depends modifier does not work

@Option(name="-u",required=true,aliases="--username",usage="Username for login")
private String username;
@Option(name="-g",forbids={"-u"},aliases="--generate",usage="generate random key")
private boolean generate;

When i use this option i get this two errors:

Options.java:24: error: cannot find symbol
@Option(name="-g",forbids={"-u"},aliases="--generate",usage="generate random key")
                  ^
symbol:   method forbids()
location: @interface Option
Options.java:24: error: illegal initializer for <none>
@Option(name="-g",forbids={"-u"},aliases="--generate",usage="generate random key")
                          ^
2 errors

CmdLineException deprecated is wrong

CmdLineException is deprecated, but instruction "Use {@link #CmdLineException(org.kohsuke.args4j.CmdLineParser, Localizable, java.lang.String...)" cannot be put to use.

Localizable interface has javadoc @see Messages

Ok, so I want to use this enum Messages:
enum Messages implements Localizable {

But this cannot be used as it can be used only inside a package (missing public keyword). Therefore there is no other option than to use deprecated CmdLineException exception.

Therefore CmdLineException should not be deprecated or Messages should be public :-)

MapSetter too restricted for custom OptionHandlers

Current MapSetter is useless for custom OptionHandler implementations. It'll only custom handlers to call it's addValue(Object value) method which assumes that value is a String in the format X=Y.

Just making "private void addValue(Object key, Object value)" public would already allow custom OptionHandler implementations to pass anything into the Map field.

Also the current MapOptionHandler only supports single arguments (-map v=n), why not multiple pairs (-map a=b x=y ...) ??

Download links on site are out of date

On the args4j homepage the download link has releases up to 2.0.21, which is more than 16 months old, but nothing newer than that. I see releases tagged up to 2.0.25 here, but can't find jars to go with them. Furthermore, the documentation on the site is for 2.0.25, e.g. OptionHandlerFilter is documented, but not available in any of the releases linked from the site.

Add ability to read arguments from an input file

Users are understandably hesitant to place sensitive arguments to an application (e.g. passwords) on the command line. A common solution is to either prompt for such arguments if not present, or to allow them to be included in an "options file".

Currently, providing this functionality in an application using args4j requires the user to implement the reading, parsing and "injecting" of the values read into the arguments returned from args4j. Also, such values cannot be marked as mandatory for args4j, so the user must perform additional validation.

It would be great if args4j could optionally read arguments from a configuration file (named by convention or provided by the user) and apply its validation etc. logic to these too.

The "myapp.args" file (or whatever it may be called) may be as simple as a Java properties file:

<argname>=<argvalue>

Remove final modifier from NamedOptionDef

I want to create an option whose usage message is derived dynamically from other info in the program. That won't work for the usage-parameter in the @option as that needs to be known at compile time. The easiest way to achieve this would be to subclass NamedOptionDef and overwrite OptionDef#usage, but that is blocked by the fact that NamedOptionDef is final.

I propose to remove the final modifier.

Add a "silent" behavior about missing or extraneous options

Not an issue, rather another use case.

args4j works great for me, thanks for sharing this piece of code.

I just would need a CmdLineParser.parseArguments() method that goes to the end of the parsing (in order to set all the options that have been given in the args[] parameter, and to not stop on the first missing option) (i have indeed some dynamic use cases where options are provided on the command line but not yet known programmaticaly, so i'm willing to read only a few parameter at first step, then do other stuff, and then read the rest of the options).

Any chance having another exception behavior for this parseArguments() method ? Otherwise i could use an extend on my side overriding this method, but a few getter/setters are missing or are private.

Add ability to recurse parsing sub-commands

Following the request found on JIRA (http://java.net/jira/browse/ARGS4J-20), I have made some fix to support basic sub-command parsing.

I copy here my answer made on JIRA


I have extended the method parseArgument() to accept a "strict" mode.
If set to false, Args4j will not complain about trailing arguments/options, and will return a list of unused arguments /options instead.

I have one class per sub-command, with proper arg4j annotations.
I take the first remaining argument, which should be the "command" and switch on it to choose the proper sub-command handler.

Then, I instantiate the sub-command class and use args4j to parse the remaining arguments against it.

Here is the piece of code that does the trick :

/**
 * Parses the command line arguments and set them to the option bean
 * given in the constructor.
 * 
 * @param args arguments to parse
 *
 * @throws CmdLineException
 *      if there's any error parsing arguments, or if
 *      {@link Option#required() required} option was not given.
 */

public String[] parseArgument(boolean strict, final String... args) throws CmdLineException {
    CmdLineImpl cmdLine = new CmdLineImpl(args);

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

    ArrayList<String> remainingArgs = new ArrayList<String>();

    while( cmdLine.hasMore() ) {
        String arg = cmdLine.getCurrentToken();


        if( isOption(arg) ) {
            boolean isKeyValuePair = arg.indexOf('=')!=-1;
            // parse this as an option.
            this.currentOptionHandler = isKeyValuePair ? findOptionHandler(arg) : findOptionByName(arg);

            if (this.currentOptionHandler==null) {
                if (strict) {
                    throw new CmdLineException(Messages.UNDEFINED_OPTION.format(arg));
                } else {
                    remainingArgs.add(arg);
                    cmdLine.proceed(1);
                    continue;
                }
            }

            // known option; skip its name
            cmdLine.proceed(1);

        } else {
            if (argIndex >= this.arguments.size()) {
                if (strict) {
                    Messages msg = this.arguments.size() == 0 ? Messages.NO_ARGUMENT_ALLOWED : Messages.TOO_MANY_ARGUMENTS;
                    throw new CmdLineException(msg.format(arg));
                } else {
                    remainingArgs.add(arg);
                    cmdLine.proceed(1);
                    continue;
                }
            }

            // known argument
            this.currentOptionHandler = this.arguments.get(argIndex);
            if (!this.currentOptionHandler.option.isMultiValued())
                argIndex++;
        }
        int diff = this.currentOptionHandler.parseArguments(cmdLine);
        cmdLine.proceed(diff);
        present.add(this.currentOptionHandler);

    } // End of loop on input arguments

    // make sure that all mandatory options are present
    for (OptionHandler handler : this.options)
        if(handler.option.required() && !present.contains(handler))
            throw new CmdLineException(Messages.REQUIRED_OPTION_MISSING.format(handler.option.toString()));

    // make sure that all mandatory arguments are present
    for (OptionHandler handler : this.arguments)
        if(handler.option.required() && !present.contains(handler))
            throw new CmdLineException(Messages.REQUIRED_ARGUMENT_MISSING.format(handler.option.toString()));

    return remainingArgs.toArray(new String[0]);
}

/** Strict mode by default */
public void parseArgument(final String... args) throws CmdLineException
{
    this.parseArgument(true, args);
}

I think this could be done more nicely, with an annotation like :

@Command(name="help", class=HelpCommand.class)
@Command(name="del", class=DelCommand.class);
@Command(name="foo", class=FooCommand.class);
@Command(name="bar", class=BarCommand.class);
AbstractSubCommand command;

This way, the framework could process additionnal checks (like ensuring that the parent options does not conflict with sub-commands options). It could also produce a nice "usage" display, with all sub-commands options.
But this would require much more work.
Hope this helps.

Raphael

Allow Alternative Option Name

Enhancement to allow more than one name to correspond to the same option.

Proposed usage example:

@Option(name="--recursive", alternativeName="-r", usage="recursively run something")
private boolean recursive;

Missing multiValued attribute in @Option

After updating to the newest args4j version I do have compile errors because of the removed "multiValued" attribute. I found the commit message which told, that it was removed because it would make no sense to have multi-value settings.

What about this case (which is simlar to mine):

@Option(name = "-in", multiValued = true, required = true)
private List<File> inFiles;

where I do support multiple ouccrences of -in (e.g. FooMain -in fileA.txt -in fileB.txt -in fileC.txt). How can this be done with the new release of args4j?

problem with default values and array

with a parameter like this one :

   @Option(name = "-c", handler = StringArrayOptionHandler.class, usage = "Set country(ies) to export", metaVar = "c1 c2 c3")
   private String[] countries2export = null;

i get this :
"-tt" is not a valid option
java SampleMain [options...]
Exception in thread "main" java.lang.NullPointerException
at java.lang.reflect.Array.getLength(Native Method)
at org.kohsuke.args4j.spi.ArrayFieldSetter.getValueList(ArrayFieldSetter.java:113)
at org.kohsuke.args4j.spi.OptionHandler.printDefaultValue(OptionHandler.java:139)
at org.kohsuke.args4j.CmdLineParser.createDefaultValuePart(CmdLineParser.java:373)
at org.kohsuke.args4j.CmdLineParser.printOption(CmdLineParser.java:350)
at org.kohsuke.args4j.CmdLineParser.printUsage(CmdLineParser.java:318)
at org.kohsuke.args4j.CmdLineParser.printUsage(CmdLineParser.java:288)
at org.kohsuke.args4j.CmdLineParser.printUsage(CmdLineParser.java:278)
at com.kompass.backoffice.export2nkc.doMain(export2nkc.java:223)
at com.kompass.backoffice.export2nkc.main(export2nkc.java:139)

i think there is a problem here (In ArrayFieldSetter):

    public List<Object> getValueList() {
        f.setAccessible(true);
        try {
             List<Object> r = new ArrayList<Object>();


            // array element might be primitive, so Arrays.asList() won't always work
            Object array = f.get(bean);
            int len = Array.getLength(array);
            for (int i=0; i<len; i++)
                r.add(Array.get(array, i));

            return r;
        } catch (IllegalAccessException ex) {
            throw new IllegalAccessError(ex.getMessage());
        }
    }

(i think i don't know all the code) that there should be a

if (array == null)
   return null;
//before the 
  int len = Array.getLength(array);

Add ability to have different methods for different arguments

I have a set of arguments in a specific order. Each needs validation applying to it, so parsing to a method instead of a variable is beneficial. So my code should look something like this:

@Argument(index = 0)
public void method0(String str){
    //method0 validation
   this.method0Var = str;
}
@Argument(index = 1)
public void method1(String str){
    //method1 validation
   this.method1Var = str;
}

Unfortunately, args4J currently presumes that arguments with a method are multivalued arguments, and thusly associates all arguments with the same method. For example, if there were 2 arguments argument0 argument1, both would be parsed with method0, when it should be possible to have each parsed with it's own relevant method.

My fix has involved editing the method isMultiValued() in org.kohsuke.args4j.spi.MethodSetter to return false instead of true, stopping the parser from using this same method for all arguments, and instead moving onto the next argument/method index

It would be nice to have this use the multiValued field of Argument (ie @Argument(index = 0, multiValue = false)</code.

Default values wrong in parser.printUsage

This MWE

import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;

public class PrintUsageBug {

    @Option(name = "-h", usage = "print help screen")
    private boolean helpScreen = false;

    public static void main(String[] args) {
        new PrintUsageBug(args);
    }

    public PrintUsageBug(String[] args) {
        CmdLineParser parser = new CmdLineParser(this);

        try {
            parser.parseArgument(args);
        } catch (CmdLineException e) {
            System.out.println(e.getMessage());
        }

        parser.printUsage(System.out);
    }

}

has the default value false for the boolean field helpScreen.

  • Running without parameters gives -h : print help screen (default: false)
  • Running with parameter -h gives -h : print help screen (default: true)

which defeats the purpose. Maybe original contributor @sfuhrm can help.

parser does not like equal signs in key=value HashMap options.

can't submit jobs to jenkins if parameters have '=' in them
e.g. JOB_PARAM="USER=damien DESTDIR=/tmp/$BUILDNUM"

here is my testcase:

import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.BooleanOptionHandler;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import java.util.Map.Entry;
import java.util.Map;
import java.util.HashMap;

public class SampleMain {
    @Option(name="-p",usage="Specify the build parameters in the key=value format.")
    Map<String,String> parameters = new HashMap<String, String>();
}

def test(String[] args) {
    println("test with args: " + args)
    main = new SampleMain()
    CmdLineParser parser = new CmdLineParser(main);
    parser.parseArgument(args)
    for (Entry<String, String> e : main.parameters.entrySet()) {
         String name = e.getKey();
         String value = e.getValue();
         println(name + ' := ' + value);
    }
}

test('-p', 'key=value')
test('-p', 'equal="i love = signs"')

here is the result:

Result

test with args: [-p, key=value]
key := value
test with args: [-p, equal="i love = signs"]
equal := "i love 

maybe related to issue #72, maybe not.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.