Command arguments
We discussed several approaches to declare arguments for commands. There are three use cases that must be supported.
- Interactive shell:
- commands should feature argument completion
- commands should allow interactive prompts
- Bndtools UI
- arguments should be grouped
- interactive prompts should be converted into multiple pages of a wizard
- Scripts
- commands should be able to run without interaction (so all optional arguments should be supplied)
- slightly related, if commands retain some kind of state, that state should be CommandSession scoped (note that this is not script specific, but will become an issue once scripts are being executed)
Metatype interfaces with Java code for conditional parameters
In this approach the arguments are grouped in interfaces.
// Simple case: one set of parameters, no interactivity @Meta.OCD interface NameAndType { @Meta.AD(name="FQN", description="The fully qualified name of the class.") FQN name(); @Meta.AD(description="The type (class or interface)") TYPE type(); } @Command() public void cmd(String... args) { NameAndType nat = m_prompt.ask(NameAndType.class, args); // arguments are available as nat.name():String and nat.type():FQN
// Complex case: multiple sets of parameters, with interactivity @Meta.OCD interface NameAndType { @Meta.AD(name="FQN", description="The fully qualified name of the class.") FQN name(); @Meta.AD(description="The type (class or interface)") TYPE type(); } Â @Meta.OCD interface DefaultMethods { @Meta.AD(description="generate toString method?") boolean string(); @Meta.AD(description="generate equals method?") boolean equals(); @Meta.AD(description="generate hashCode method?") boolean hashcode(); } @Command() public void cmd(String... args) { NameAndType nat = m_prompt.ask(NameAndType.class, args); if (nat.type().equals(CLASS)) { DefaultMethods methods = m_prompt.ask(DefaultMethods.class, args); }
Upsides:
- Questions are grouped, this is ideal for generating wizards in Bndtools
- Don't need multiple methods for commands that have optional parameters.
- No special things required for scripting, script can just provide all parameters.
Downsides:
- The questions are "disconnected" from the command implementations. This is less obvious to program.
- Difficult to support command completion
Annotated arguments
// Simple case: one set of parameters, no interactivity @Command() public void cmd(@Argument(name="name", description="The fully qualified name of the class.") String name, @Argument(description="The type (class or interface)") FQN type) { // arguments are available as name:String and type:FQN
// Complex case: multiple sets of parameters, with interactivity @Command() public void cmd(@Argument(name="name", description="The fully qualified name of the class.") String name, @Argument(description="The type (class or interface)") FQN type) { if (type.equals(CLASS)) { boolean genString = m_prompt.ask("generate toString method?", Boolean.class, "string"); boolean genEquals = m_prompt.ask("generate equals method?", Boolean.class, "equals"); boolean genHashcode = m_prompt.ask("generate hashCode method?", Boolean.class, "hashcode");
Upsides:
- Obvious to implement.
- Relatively easy to support both passing arguments immediately, and asking the user for answers during execution of the command, you just write different implementations of your method.
- Easy to implement argument completion for the initial set of arguments and the interactive prompts separately, hard to do completion on a single command line for all possible arguments.
Downsides:
- No way to group questions, besides just showing all of them in the same wizard. Note that this is only a problem when multiple wizard pages are required in Bndtools (the complex case).
- No argument completion for the total set of options.
- Needs multiple methods for optional arguments.
- Scripting needs extra recorder, which adds state somewhere that can't be transferred through the arguments of the command/method.
Metatype interfaces with DSL for conditional parameters
// Simple case: one set of parameters, no interactivity @Meta.OCD interface NameAndType { @Meta.AD(name="FQN", description="The fully qualified name of the class.") FQN name(); @Meta.AD(description="The type (class or interface)") TYPE type(); } @Command() public void cmd(@Prompt(NameAndType.class) String... args) { NameAndType nat = m_prompt.ask(NameAndType.class, args);
// Complex case: multiple sets of parameters, with interactivity @Meta.OCD interface NameAndType { @Meta.AD(name="FQN", description="The fully qualified name of the class.") FQN name(); @Meta.AD(description="The type (class or interface)") TYPE type(); @Condition("type=CLASS") DefaultMethods methods(); } @Meta.OCD interface DefaultMethods { @Meta.AD(description="generate toString method?") boolean string(); @Meta.AD(description="generate equals method?") boolean equals(); @Meta.AD(description="generate hashCode method?") boolean hashcode(); } @Command() public void cmd(@Prompt(NameAndType.class) String... args) { NameAndType nat = m_prompt.ask(NameAndType.class, args); Â
Upsides:
- Questions are grouped, this is ideal for generating wizards in Bndtools.
- Argument completion for all parameters of all parts of the interaction.
- In a UI, all wizard pages can be generated in one go.
- Don't need multiple methods for commands that have optional parameters.
- No special things required for scripting, script can just provide all parameters.
Downsides:
- The questions are "disconnected" from the command implementations. This is less obvious to program.
- The "DSL" that describes the "@Condition" is a more limited than Java code.
Â