kohsuke / args4j Goto Github PK
View Code? Open in Web Editor NEWargs4j
Home Page: http://args4j.kohsuke.org/
License: MIT License
args4j
Home Page: http://args4j.kohsuke.org/
License: MIT License
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.
Multiple OptionHandler
sub-classes use Parameters#getParameter(...)
with -1
as the index parameter, e.g. EnumOptionHandler
.
This approach does not work when the "key=value" syntax is used, see splitToken()
in CmdLineParser.CmdLineImpl
for why this is not working.
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.
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
getOptionName
org.kohsuke.args4j.Messages
Would you consider a PR for this functionality? Should I do one?
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.
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.
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).
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.
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.
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?
The only Quick Start available uses the following link:
http://weblogs.java.net/blog/kohsuke/archive/2005/05/parsing_command.html
That link no longer seems to have content---only comments.
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)
It would be very useful if subclasses can throw an exception in this method in case the key or value is incorrect for the specific option. I don't think this will break compatibility since it's supposed to be a callback method only.
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
}
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.
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
As merging SHA: b972e18 , it needs JDK7 to compile.
Is the JDK 7 base version on arg4j?
How can I build 2.0.25 version or later with JDK6 ?
Anyway I'm very appreciate your effort.
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)
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.
Apidocs is so old: http://args4j.kohsuke.org/args4j/apidocs/
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
You cannot create DelimitedOptionHandler<String>
because the individualOptionHandler
must derive from OneArgumentOptionHandler
which StringOptionHandler
does not.
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!
It would be great if args4j could print the default values (i.e. the values that are already assigned to the POJO fields) when printing the usage help to e.g., the console using CmdLineParser.printUsage();
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 a file with a short description of the bugs fixed/features added for each release... either in the repo or the website.
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!
Logging a question as an issue.
Hi,
The annotation processor in the args4j-tools module fails to compile with Java 8 because it relies on the APT classes which were removed. The processor should be rewritten to use the javax.annotation.processing
API introduced in Java 6.
For more information:
http://docs.oracle.com/javase/7/docs/technotes/guides/apt/GettingStarted.html
http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#processing
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;
@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 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 :-)
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 ...) ??
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.
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>
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.
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.
Introduced in fc9dfc9
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
Hello,
we use Args4j and have to integrate it in an OSGI platform.
Would you accept a pull request to bundle-ize args4j ?
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;
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?
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);
Most recent artifact is 2.0.29:
https://repo1.maven.org/maven2/args4j/args4j/
However, args4j-maven-plugin is 2.0.30:
https://repo1.maven.org/maven2/org/kohsuke/args4j/args4j-maven-plugin/
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.
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
.
-h : print help screen (default: false)
-h
gives -h : print help screen (default: true)
which defeats the purpose. Maybe original contributor @sfuhrm can help.
ResourceBundle#getString(String) never returns null
, so this code in OptionHandler#getMetaVariable(...)
should be changed to use ResourceBundle#containsKey(String).
README.md says the following:
"Download the distribution or include the library from sthe Maven Repository at java.net." with an associated hyperlink of: http://maven.glassfish.org/content/groups/public/args4j/
However, this link does not appear to work and leads to a 404.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.