Looking for an API to know all the commands defined. This can be helpful to print help message of all the commands in case customer asks for global help of all the commands
Please take a look at the CommandLine::getSubcommands method: https://github.com/remkop/picocli/blob/master/src/main/java/picocli/CommandLine.java#L185
Also, by default CommandLine::usage will print the list of subcommands available for the current command.
Is there similar api to to know all the commands i.e which are annotated with @Command
You mean in the classpath? You could iterate over all classes and query them with cls.isAnnotationPresent(Command.class) but there are so many classes in the classpath that this would be very slow...
It may be easier to think of it this way: a Java program is started with a number of command line arguments. Think of the Java program as the top level command. In picocli this is the object that the CommandLine was constructed with.
Your program can understand some subcommands (and each of these can have nested sub-subcommands to any depth). When you create your program you know what subcommands it can handle. For each of these you call commandLine.addSubcommand(subcommandName, subcommandObject). Since you created the program you know what subcommands exist.
Instead of parsing every class, would it be possible for an api, which can do this during the annotation processing. This way, extra overheads at run time can be reduced
Thanks for the interesting feedback and discussion on this ticket and #155.
Currently, picocli is designed to have a hierarchy of subcommands in a tree structure, with a single CommandLine object at the top of the hierarchy. In this design, applications can always query the top-level CommandLine object to get a map of the registered subcommands. Each of these subcommands can be further queried for the more deeply nested sub-sub commands, etc.
I am aware of your proposal in #152 to register subcommands via annotations. The question raised in this ticket (API to know all commands) is one of the questions that need to be answered if we were to register subcommands via annotations. Perhaps we should make a note of that in the #152 ticket.
Given the above explanation of the current picocli design, are you ok to close this question?
This ticket API need not be linked to sub commands tickets #152, as we any way need to know all the main commands in a given application.
(Sorry, I pressed the wrong button. Didn't mean to close this issue. )
Let's clarify our terminology to make sure we are talking about the same thing.
In my view, a Java application has an entry point (the main method) which corresponds 1-on-1 with the main command.
The name of the main command is not in the String[] array passed to the main(String[] args) method. What appears in the argument array are subcommands, options and nested sub-subcommands.
In this terminology, each Java application (a class with a main method) has only one main command.
Does that match what you call main commands and subcommands?
Remkop, the API needed for the situation as said in #155
i.e for usage of CLI in the following way
java -cp myApp.jar com.teja.Application create_parking 213
java -cp myApp.jar com.teja.Application park car white ka05ma2456
java -cp myApp.jar com.teja.Application leave 4
java -cp myApp.jar com.teja.Application status
My observation in cli applications is that, Java Class (only one main method for all the CLI commands) and jar would be same. The subsequent parameter would be the main command. In above example, i see _create_parking
park
leave
status_
as main commands.
i.e. there is only one main method in the application, which knows what the main cli command is based on args[0] i.e first argument.
In picocli, create_parking, park, leave etc are _subcommands_, not main commands. This is documented in the picocli user manual, please see 10.2. Parsing Subcommands.
I should update the documentation to make this more clear.
I took a look at your project.
I now understand your use case a bit better. Basically, when the user requests help, you want to print the usage() of the subcommand that was invoked, and also print a list of all available commands. To accomplish the latter, you want to call usage() on the main command. The problem is that the subcommand does not have a reference to the main command.
You solved this by taking the first CommandLine main command object from the list returned by parse, and passing this to the subcommand before calling execute on the subcommand.
I think that looks pretty good. If I can make a suggestion: why not pass the first CommandLine main command object as a parameter to the execute method of the subcommand that was invoked? That way you no longer need to call lSubCommand.setRootCMDLine(lRootCmdLine) and you can even remove the AbstCLICmd.rootCMDLine field altogether.
Something like this:
// class CMDUtils
public static final void invokeCLICmd(final String[] aArgs) throws Exception {
List<CommandLine> list = createCLISpec().parse(aArgs);
assert list.size() == 2;
AbstCLICmd lSubCommand = ((AbstCLICmd) list.get(1).getCommand());
// pass the main command so we can show list of available commands
lSubCommand.execute(list.get(0));
}
...
The execute method signature would look like this:
public interface CLICmd {
public CLICmd execute(CommandLine allCommands) throws Exception;
...
}
// class AbstCLICmd
@Override
public final CLICmd execute(CommandLine allCommands) throws Exception {
if (isHelpRequested()) {
CommandLine.usage(this, System.err);
allCommands.usage(System.err); // show list of available commands
}
...
Alternatively, you could pass the full List<CommandLine> to the execute method, but perhaps that is overkill.
Glad that you looked at my code.. Thank you so much.
b.t.w, this repository was supposed to private rep, which I have made it now else where.
I can privately share you the code.. in case you already don't have
I can follow your suggestion.. however, passing the reference in all the methods (I am assuming that, one of the called method also needs reference of allCommands in some context) called by execute() also demands change of signature to take allCommands reference as parameter. which is not advised..
In my code, I see the following as issues, w.r.t CLI, which i wish picoCLI to consider to enhance its api
@Command(name = Leave.LEAVE_CMD, description = "remove the vehicle from parking lot", subcommandof=RootCmd.class) This can potentially eliminate long api calls and easy to maintain relationships with annotations than with codeCommandLine::getParentCommand() returning a CommandLine.addSubcommand() is covered by #152. Need to think about this more.3 & 4 use case I have discussed above
java -cp myApp.jar com.teja.Application create_parking 213
java -cp myApp.jar com.teja.Application park car white ka05ma2456
java -cp myApp.jar com.teja.Application leave 4
java -cp myApp.jar com.teja.Application status
In such cli application where main method is only one, how do we print all commands help message (don't wish to create a virtual command)
Thanks for the clarification.
Can we rename this issue to "Add getParentCommand API", and rename issue #155 to "API to know all commands"?
Sure. Thank U.
CommandLine::getParent (see #157). Please see if this works for you.addSubcommand() is implemented in #152. Please see if this works for you.sure Rem, is the latest jar pushed to maven
I only see June update here (https://mvnrepository.com/artifact/info.picocli/picocli)
I didn't make a release yet. Can you check out and build master to confirm?
Tested with 0.9.8-SNAPSHOT.jar local jar, every thing works.
Thank you. it would be nice if you could push *-SNAPSHOT.jar to maven rep.
Thanks for the confirmation.
I'm thinking to release 0.9.8 soon, hopefully this weekend.
I am sure, most may not like it, However I do wish in similar lines to getParent(), if getCommand()method CommandLinereturns @command object, which would also have reference to CommandLinereference.
My thoughts on this is @command object is domain Object, it is nice to deal with this reference everywhere in application/solution rather than referring/passing CommadLineeverywhere.
I see your point. It would be nice to deal with the domain objects directly everywhere.
We added getParent so that CommandLine objects can traverse the subcommand hierarchy in both directions.
For domain objects to be able to do this, they would need to implement some interface, but I don't want picocli to impose any restrictions on the domain objects.
Ok then, on the the last point, the title of this issue:
@CommandYou mention you need this in order to print a help message with all commands.
Picocli gives you that ability if you create a main command and register your commands as subcommands. What I don't understand is why you don't want to do this.
Furthermore, what if you have a JAR with two command line applications, each with a different set of subcommands? Automatically picking up all @Command classes would incorrectly mix all these classes.
I really don't see any benefit in doing this...
Creating main command and adding every others as sub command is a hack , also code is saying wrong relationships which are not real, but created to print messages of all commands.
Presence of query API may avoid one main command class all together in every CLI application.
No, this is not a hack.
Every picocli application has a main command, even CLIs without subcommands.
For example, this application has a main command without subcommands:
java -cp myjar.jar com.example.Zip -file /path/to/file
Note that the user never specifies the name of the main @command on the command line. The main command is _always implicit_ on the command line.
In your examples:
java -cp myApp.jar com.teja.Application status
The user needs to specify status (the name of the Status @command) on the command line. That means status is a subcommand, not a main command. It must be registered as a subcommand if you want to use picocli to parse the command line.
Note that this is not just about printing a list of all commands.
Your Application is a command line application, and as such it should probably have a usage help message by itself, so users can do the following and get general information about what the application does:
java -cp myApp.jar com.teja.Application --help
Your Application may support other global options, like --verbose, etcetera, which apply to _all_ subcommands.
java -cp myApp.jar com.teja.Application --verbose park car white ka05ma2456
^ ^ ^ ^
| | | |
main command global sub- parameter to subcommand
option command
By making Application (or some other internal class) the main command, and status and park subcommands of that main command, you get all the above behaviour out of the box. This is the natural way to model a CLI with subcommands like your application. It is not a hack.
(By the way, picocli-0.9.8 is now available for download from Maven Central. The user manual is also updated.)
It is the time, we start re-defining main command differently in the context of picocli as it specific to java applications.
I see com.teja.Application as argument passed to java. It has no relevance to picocli framework main command.
Assume the other scenario too, what if I have a way to convert com.teja.Application into exe file say app.exe. Then I shall invoke the same command
app.exe --verbose park car white ka05ma2456
My understanding is app.exe as application not as main command unless com.teja.Application is annotated with @Command. CLI framework, should not enforce that com.teja.Application has to be annotated with @Command.
The way I wish PICOCLI to view main command is, any class which is annotated with @Command annotation and potentially, we can take subcommands as the ones which are also annotated with @Command and also added to another @Command class through::addSubcommand()or with respective annotation.
Doing what you're asking for would re-introduce a host of problems that are already solved with the current main command-subcommand hierarchy.
What you describe as the benefit (ability to avoid one main command class all together in every CLI application) does not seem like a big problem. In fact, I am convinced that as soon as your application starts to have global options and global help, you will find that omitting the central main command is not even desirable and changing the design to support this would be a mistake.
I'm going to mark this feature request as "won't fix" for now, until someone can show me a use case that cannot be accomplished with the current main command-subcommand hierarchy. (I mean, "I just don't want to create a central main command" is not enough reason for me to do this.)
But again, thank you for the interesting discussions and good suggestions! Thanks to your input picocli now has a getParent method and the ability to declaratively add subcommands via annotations, and I credited these to you in the release notes. Also, later I'll work to make subcommands case-insensitive.
Thanks for your contributions, I greatly appreciate them!
(I mean, "I just don't want to create a central main command" is not enough reason for me to do this.)
Ok, if PICOCli wishes to enforce this, hope it could be documented, specially on the definition of main command which is always the first argument to java app, it needs to have @Command and it also needs to have subcommands attribute taking all the commands in that application and has main() method.
Sure, let me know what section in the user manual specifically needs to be clarified.
I am open to pull requests for documentation improvements.
This section can be refined http://picocli.info/#_introduction
public class Example {
can be made as
@Command(subcommands= {})
public class Example {
public static void main(String[] args){
}
}